<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 7.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.ico">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.ico">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha256-wiz7ZSCn/btzhjKDQBms9Hx4sSeUYsDrTLg7roPstac=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.css" integrity="sha256-6cQIC71/iBIYXFK+0RHAvwmjwWzkWd+r7v/BX3/vZDc=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/themes/green/pace-theme-minimal.css">
  <script src="https://cdnjs.cloudflare.com/ajax/libs/pace/1.2.4/pace.min.js" integrity="sha256-gqd7YTjg/BtfqWSwsJOvndl0Bxc8gFImLEkXQT8+qj0=" crossorigin="anonymous"></script>

<script class="next-config" data-name="main" type="application/json">{"hostname":"sumumm.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.19.2","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":true,"style":"mac"},"fold":{"enable":true,"height":300},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":true,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":true,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta name="description" content="本文主要是使用repo——repo基本原理的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:type" content="article">
<meta property="og:title" content="LV03-repo-02-repo基本原理">
<meta property="og:url" content="https://sumumm.github.io/post/d94f8cde.html">
<meta property="og:site_name" content="苏木">
<meta property="og:description" content="本文主要是使用repo——repo基本原理的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926231102172.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926231714368.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232134894.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232219048.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232316884.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232355572.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232520935.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232626470.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927063837299.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927063525511.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927064108549.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927064649242.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927070242090.png">
<meta property="og:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926233044031.png">
<meta property="article:published_time" content="2024-10-26T14:40:57.000Z">
<meta property="article:modified_time" content="2025-06-13T16:25:57.071Z">
<meta property="article:author" content="苏木">
<meta property="article:tag" content="Git">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926231102172.png">


<link rel="canonical" href="https://sumumm.github.io/post/d94f8cde.html">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":"","permalink":"https://sumumm.github.io/post/d94f8cde.html","path":"post/d94f8cde.html","title":"LV03-repo-02-repo基本原理"}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>LV03-repo-02-repo基本原理 | 苏木</title>
  








    <script src="/js/browser_tools_disable.js"></script>

  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
<!-- hexo injector head_end start --><link rel="stylesheet" href="https://unpkg.com/hexo-next-tags-plus@latest/lib/tag_plus.css" media="defer" onload="this.media='all'"><!-- hexo injector head_end end --></head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <p class="site-title">苏木</p>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">我的学习之路</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>苏木的家</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类页<span class="badge">42</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档页<span class="badge">673</span></a></li><li class="menu-item menu-item-flink"><a href="/flink/" rel="section"><i class="fa fa-link fa-fw"></i>友人帐</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于我</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
            <div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%B8%80%E3%80%81%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86"><span class="nav-text">一、基本原理</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E5%85%88%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%80%E4%B8%AArepo%E5%AE%A2%E6%88%B7%E7%AB%AF"><span class="nav-text">1. 先初始化一个repo客户端</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E5%91%BD%E4%BB%A4%E6%80%8E%E4%B9%88%E6%89%A7%E8%A1%8C%E7%9A%84%EF%BC%9F"><span class="nav-text">2. 命令怎么执行的？</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#2-1-repo%E4%BB%93%E5%BA%93"><span class="nav-text">2.1 repo仓库</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-1-ParseArguments"><span class="nav-text">2.1.1 _ParseArguments()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-2-FindRepo"><span class="nav-text">2.1.2 _FindRepo</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-3-RunSelf"><span class="nav-text">2.1.3 _RunSelf()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-4-SetDefaultsTo"><span class="nav-text">2.1.4 _SetDefaultsTo()</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#2-1-5-Init"><span class="nav-text">2.1.5 _Init()</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-3-Manifest%E4%BB%93%E5%BA%93"><span class="nav-text">2.3 Manifest仓库</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-4-AOSP%E5%AD%90%E9%A1%B9%E7%9B%AE%E4%BB%93%E5%BA%93"><span class="nav-text">2.4 AOSP子项目仓库</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#2-5-%E5%9C%A8AOSP%E4%B8%8A%E5%88%9B%E5%BB%BATopic"><span class="nav-text">2.5 在AOSP上创建Topic</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#%E4%BA%8C%E3%80%81-repo%E7%9B%B8%E5%85%B3%E7%9B%AE%E5%BD%95"><span class="nav-text">二、.repo相关目录</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E9%A1%B9%E7%9B%AE%E6%B8%85%E5%8D%95%E5%BA%93-repo-manifests"><span class="nav-text">1. 项目清单库(.repo&#x2F;manifests)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-repo%E8%84%9A%E6%9C%AC%E5%BA%93-repo-repo"><span class="nav-text">2. repo脚本库(.repo&#x2F;repo)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E4%BB%93%E5%BA%93%E7%9B%AE%E5%BD%95%E5%92%8C%E5%B7%A5%E4%BD%9C%E7%9B%AE%E5%BD%95"><span class="nav-text">3. 仓库目录和工作目录</span></a></li></ol></li></ol></div>
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="苏木"
      src="/images/avatar.jpg">
  <p class="site-author-name" itemprop="name">苏木</p>
  <div class="site-description" itemprop="description">莫道桑榆晚，为霞尚满天</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">673</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">42</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">43</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author animated">
      <span class="links-of-author-item">
        <a href="https://github.com/sumumm" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;sumumm" rel="noopener me" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
  </div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner post posts-expand">


  


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="https://sumumm.github.io/post/d94f8cde.html">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.jpg">
      <meta itemprop="name" content="苏木">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="苏木">
      <meta itemprop="description" content="莫道桑榆晚，为霞尚满天">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="LV03-repo-02-repo基本原理 | 苏木">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          LV03-repo-02-repo基本原理
        </h1>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2024-10-26 22:40:57" itemprop="dateCreated datePublished" datetime="2024-10-26T22:40:57+08:00">2024-10-26</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/" itemprop="url" rel="index"><span itemprop="name">开发工具</span></a>
        </span>
          ，
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/" itemprop="url" rel="index"><span itemprop="name">03版本控制</span></a>
        </span>
    </span>

  
    <span class="post-meta-break"></span>
    <span class="post-meta-item" title="本文字数">
      <span class="post-meta-item-icon">
        <i class="far fa-file-word"></i>
      </span>
      <span class="post-meta-item-text">本文字数：</span>
      <span>15k</span>
    </span>
    <span class="post-meta-item" title="阅读时长">
      <span class="post-meta-item-icon">
        <i class="far fa-clock"></i>
      </span>
      <span class="post-meta-item-text">阅读时长 &asymp;</span>
      <span>55 分钟</span>
    </span>
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody"><p>本文主要是使用repo——repo基本原理的相关笔记，若笔记中有错误或者不合适的地方，欢迎批评指正😃。</p>
<span id="more"></span>

<!-- Photo: https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/ -->

<details class="folding-tag" blue><summary> 点击查看使用工具及版本 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center" rowspan="5">PC端开发环境</td>        <td align="center" width=150px>Windows</td>        <td align="left">Windows11</td>    </tr>    <tr>        <td align="center">Ubuntu</td>        <td align="left">Ubuntu20.04.2的64位版本</td>      </tr>    <tr>        <td align="center">VMware® Workstation 17 Pro</td>        <td align="left">17.6.0 build-24238078</td>      </tr>    <tr>        <td align="center">终端软件</td>        <td align="left">MobaXterm(Professional Edition v23.0 Build 5042 (license))</td>    </tr>    <tr>        <td align="center">Win32DiskImager</td>        <td align="left">Win32DiskImager v1.0</td>      </tr>    <tr>        <td align="center" rowspan="3">Linux开发板环境</td>        <td align="center">Linux开发板</td>        <td align="left">正点原子 i.MX6ULL Linux 阿尔法开发板</td>      </tr>    <tr>        <td align="center">uboot</td>        <td align="left">NXP官方提供的uboot，NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)</td>      </tr>    <tr>        <td align="center">linux内核</td>        <td align="left">linux-4.15(NXP官方提供)</td>      </tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看本文参考资料 </summary>
              <div class='content'>
              <table>    <tr><td align="center">参考资料  </td><td align="center">相关说明</td></tr>    <tr><td align="left">Git 官网</td><td align="left"><a href="https://git-scm.com/" target="_blank">https://git-scm.com/ <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="left">Git 官方文档</td><td align="left"><a href="https://git-scm.com/doc" target="_blank">https://git-scm.com/doc <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="left">Pro Git Book</td><td align="left"><a href="https://git-scm.com/book/zh/v2" target="_blank">https://git-scm.com/book/zh/v2 <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="left">Git 快速使用指南</td><td align="left"><a href="https://training.github.com/downloads/zh_CN/github-git-cheat-sheet/" target="_blank">https://training.github.com/downloads/zh_CN/github-git-cheat-sheet/ <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="left">Visual Git Cheat Sheet</td><td align="left"><a href="https://ndpsoftware.com/git-cheatsheet.html#loc=index" target="_blank">https://ndpsoftware.com/git-cheatsheet.html#loc=index <i class="fa fa-external-link-alt"></i> </a></td></tr>    <tr><td align="left">Repo 工具使用介绍 - Gitee.com</td><td align="left"><a href="https://gitee.com/help/articles/4316#article-header0" target="_blank">https://gitee.com/help/articles/4316#article-header0 <i class="fa fa-external-link-alt"></i> </a></td></tr></table>
              </div>
            </details>

<details class="folding-tag" blue><summary> 点击查看相关文件下载 </summary>
              <div class='content'>
              <table>    <tr>        <td align="center">分类</td>        <td align="center">网址</td>        <td align="center">说明</td>    </tr>    <tr>        <td align="center" rowspan="0">---</td>        <td align="left"><a href="" target="_blank">---</a></td>        <td align="left">---</td>    </tr></table>
              </div>
            </details>

<h1 id="一、基本原理"><a href="#一、基本原理" class="headerlink" title="一、基本原理"></a><font size=3>一、基本原理</font></h1><img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926231102172.png" alt="image-20240926231102172" style="zoom: 50%;" />

<p>前面我们知道repo需要关注当前git库的数量、名称、路径等，有了这些基本信息，才能对这些git库进行操作。通过集中维护所有git库的清单，repo可以方便的从清单中获取git库的信息。 这份清单会随着版本演进升级而产生变化，同时也有一些本地的修改定制需求，所以，repo是通过一个git库来管理项目的清单文件的，这个git库名字叫manifests。</p>
<p>当打开repo这个可执行的python脚本后，发现代码量并不大(不超过1000行)，难道仅这一个脚本就完成了AOSP数百个git库的管理吗？并非如此。 repo是一系列脚本的集合，这些脚本也是通过git库来维护的，这个git库名字叫repo。</p>
<p>在客户端使用repo初始化一个项目时，就会从远程把manifests和repo这两个git库拷贝到本地，但这对于Android开发人员来说，又是近乎无形的(一般通过文件管理器，是无法看到这两个git库的)。 repo将自动化的管理信息都隐藏根目录的.repo子目录中。</p>
<h2 id="1-先初始化一个repo客户端"><a href="#1-先初始化一个repo客户端" class="headerlink" title="1. 先初始化一个repo客户端"></a><font size=3>1. 先初始化一个repo客户端</font></h2><p>这里我们直接参考安卓的源码文档：<a target="_blank" rel="noopener" href="https://source.android.google.cn/docs/setup/download/downloading?hl=zh-cn">下载源代码  | Android Open Source Project (google.cn)</a>，从文档中可知，我们可以运行repo init 获取最新版本的 Repo 及其最新的 bug 修复。Android 源代码中包含的各个仓库在工作目录中的放置位置是通过清单文件来指定的，必须为该清单指定一个网址。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo init -u https://android.googlesource.com/platform/manifest</span><br></pre></td></tr></table></figure>

<p>这个命令实际上是包含了两个操作：安装Repo仓库和Manifest仓库。其中，Manifest仓库的地址由-u后来带的参数给出。我们执行后，不出意外的话就出意外了：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926231714368.png" alt="image-20240926231714368" style="zoom:50%;" />

<p>这里大概还是因为网络的问题，我们换用国内的：<a target="_blank" rel="noopener" href="https://mirrors.tuna.tsinghua.edu.cn/help/AOSP/">AOSP | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a>，从这里可知，我们可以执行下面这个命令：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest</span><br></pre></td></tr></table></figure>

<p>不出意外的话，又又又出意外了：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232134894.png" alt="image-20240926232134894" style="zoom:50%;" />

<p>这里我们看清华源哪里的帮助文档吧：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232219048.png" alt="image-20240926232219048" style="zoom:50%;" />

<p>我们来到帮助页面<a target="_blank" rel="noopener" href="https://mirrors.tuna.tsinghua.edu.cn/help/git-repo/">git-repo | 镜像站使用帮助 | 清华大学开源软件镜像站 | Tsinghua Open Source Mirror</a>：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232316884.png" alt="image-20240926232316884" style="zoom:50%;" />

<p>那我们就按照这里说的操作吧，搞完之后，就可以成功啦：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232355572.png" alt="image-20240926232355572" style="zoom:50%;" />

<p>其实这里要是自己在gitee上fork了repo源码仓库的花，也可以写成自己的。图中框出来的地方我选了y。然后我们看一下都生成了什么：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232520935.png" alt="image-20240926232520935" style="zoom:50%;" />

<p>会发现多了一个.repo的目录，我们来看一下目录里面都有什么：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926232626470.png" alt="image-20240926232626470" style="zoom:50%;" />

<h2 id="2-命令怎么执行的？"><a href="#2-命令怎么执行的？" class="headerlink" title="2. 命令怎么执行的？"></a><font size=3>2. 命令怎么执行的？</font></h2><p>接下来，我们来看看明明怎么执行的，怎么得到上面哪些目录的。这个命令实际上是包含了两个操作：安装Repo仓库和Manifest仓库。其中，Manifest仓库的地址由-u后来带的参数给出。</p>
<h3 id="2-1-repo仓库"><a href="#2-1-repo仓库" class="headerlink" title="2.1 repo仓库"></a><font size=3>2.1 repo仓库</font></h3><p>我们看看Repo脚本是如何执行repo init命令的：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">main</span>(<span class="params">orig_args</span>):</span><br><span class="line">    cmd, opt, args = _ParseArguments(orig_args)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># We run this early as we run some git commands ourselves.</span></span><br><span class="line">    SetGitTrace2ParentSid()</span><br><span class="line"></span><br><span class="line">    repo_main, rel_repo_dir = <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line">    <span class="comment"># Don&#x27;t use the local repo copy, make sure to switch to the gitc client first.</span></span><br><span class="line">    <span class="keyword">if</span> cmd != <span class="string">&quot;gitc-init&quot;</span>:</span><br><span class="line">        repo_main, rel_repo_dir = _FindRepo()</span><br><span class="line"></span><br><span class="line">    wrapper_path = os.path.abspath(__file__)</span><br><span class="line">    my_main, my_git = _RunSelf(wrapper_path)</span><br><span class="line"></span><br><span class="line">    cwd = os.getcwd()</span><br><span class="line">    <span class="keyword">if</span> get_gitc_manifest_dir() <span class="keyword">and</span> cwd.startswith(get_gitc_manifest_dir()):</span><br><span class="line">        <span class="built_in">print</span>(</span><br><span class="line">            <span class="string">&quot;error: repo cannot be used in the GITC local manifest directory.&quot;</span></span><br><span class="line">            <span class="string">&quot;\nIf you want to work on this GITC client please rerun this &quot;</span></span><br><span class="line">            <span class="string">&quot;command from the corresponding client under /gitc/&quot;</span>,</span><br><span class="line">            file=sys.stderr,</span><br><span class="line">        )</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> repo_main:</span><br><span class="line">        <span class="comment"># Only expand aliases here since we&#x27;ll be parsing the CLI ourselves.</span></span><br><span class="line">        <span class="comment"># If we had repo_main, alias expansion would happen in main.py.</span></span><br><span class="line">        cmd, alias_args = _ExpandAlias(cmd)</span><br><span class="line">        args = alias_args + args</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> opt.<span class="built_in">help</span>:</span><br><span class="line">            _Usage()</span><br><span class="line">        <span class="keyword">if</span> cmd == <span class="string">&quot;help&quot;</span>:</span><br><span class="line">            _Help(args)</span><br><span class="line">        <span class="keyword">if</span> opt.version <span class="keyword">or</span> cmd == <span class="string">&quot;version&quot;</span>:</span><br><span class="line">            _Version()</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> cmd:</span><br><span class="line">            _NotInstalled()</span><br><span class="line">        <span class="keyword">if</span> cmd == <span class="string">&quot;init&quot;</span> <span class="keyword">or</span> cmd == <span class="string">&quot;gitc-init&quot;</span>:</span><br><span class="line">            <span class="keyword">if</span> my_git:</span><br><span class="line">                _SetDefaultsTo(my_git)</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                _Init(args, gitc_init=(cmd == <span class="string">&quot;gitc-init&quot;</span>))</span><br><span class="line">            <span class="keyword">except</span> CloneFailure:</span><br><span class="line">                path = os.path.join(repodir, S_repo)</span><br><span class="line">                <span class="built_in">print</span>(</span><br><span class="line">                    <span class="string">&quot;fatal: cloning the git-repo repository failed, will remove &quot;</span></span><br><span class="line">                    <span class="string">&quot;&#x27;%s&#x27; &quot;</span> % path,</span><br><span class="line">                    file=sys.stderr,</span><br><span class="line">                )</span><br><span class="line">                shutil.rmtree(path, ignore_errors=<span class="literal">True</span>)</span><br><span class="line">                shutil.rmtree(path + <span class="string">&quot;.tmp&quot;</span>, ignore_errors=<span class="literal">True</span>)</span><br><span class="line">                sys.exit(<span class="number">1</span>)</span><br><span class="line">            repo_main, rel_repo_dir = _FindRepo()</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            _NoCommands(cmd)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> my_main:</span><br><span class="line">        repo_main = my_main</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> repo_main:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;fatal: unable to find repo entry point&quot;</span>, file=sys.stderr)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    reqs = Requirements.from_dir(os.path.dirname(repo_main))</span><br><span class="line">    <span class="keyword">if</span> reqs:</span><br><span class="line">        reqs.assert_all()</span><br><span class="line"></span><br><span class="line">    <span class="comment"># Python 3.11 introduces PYTHONSAFEPATH and the -P flag which, if enabled,</span></span><br><span class="line">    <span class="comment"># does not prepend the script&#x27;s directory to sys.path by default.</span></span><br><span class="line">    <span class="comment"># repo relies on this import path, so add directory of REPO_MAIN to</span></span><br><span class="line">    <span class="comment"># PYTHONPATH so that this continues to work when PYTHONSAFEPATH is enabled.</span></span><br><span class="line">    python_paths = os.environ.get(<span class="string">&quot;PYTHONPATH&quot;</span>, <span class="string">&quot;&quot;</span>).split(os.pathsep)</span><br><span class="line">    new_python_paths = [os.path.join(rel_repo_dir, S_repo)] + python_paths</span><br><span class="line">    os.environ[<span class="string">&quot;PYTHONPATH&quot;</span>] = os.pathsep.join(new_python_paths)</span><br><span class="line"></span><br><span class="line">    ver_str = <span class="string">&quot;.&quot;</span>.join(<span class="built_in">map</span>(<span class="built_in">str</span>, VERSION))</span><br><span class="line">    me = [</span><br><span class="line">        sys.executable,</span><br><span class="line">        repo_main,</span><br><span class="line">        <span class="string">&quot;--repo-dir=%s&quot;</span> % rel_repo_dir,</span><br><span class="line">        <span class="string">&quot;--wrapper-version=%s&quot;</span> % ver_str,</span><br><span class="line">        <span class="string">&quot;--wrapper-path=%s&quot;</span> % wrapper_path,</span><br><span class="line">        <span class="string">&quot;--&quot;</span>,</span><br><span class="line">    ]</span><br><span class="line">    me.extend(orig_args)</span><br><span class="line">    exec_command(me)</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;fatal: unable to start %s&quot;</span> % repo_main, file=sys.stderr)</span><br><span class="line">    sys.exit(<span class="number">148</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main(sys.argv[<span class="number">1</span>:])</span><br></pre></td></tr></table></figure>

<h4 id="2-1-1-ParseArguments"><a href="#2-1-1-ParseArguments" class="headerlink" title="2.1.1 _ParseArguments()"></a><font size=3>2.1.1 _ParseArguments()</font></h4><p>_ParseArguments无非是对Repo的参数进行解析，得到要执行的命令及其对应的参数例如，当我们调用下面这个命令的时候：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo init -u https://android.googlesource.com/platform/manifest</span><br></pre></td></tr></table></figure>

<p>就表示要执行的命令是init，这个命令后面跟的参数是<code>-u https://android.googlesource.com/platform/manifest</code>。同时，如果我们调用“repo sync”，就表示要执行的命令是sync，这个命令没有参数。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927063837299.png" alt="image-20240927063837299" />

<p>_ParseArguments函数实现如下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_ParseArguments</span>(<span class="params">args</span>):</span><br><span class="line">    cmd = <span class="literal">None</span></span><br><span class="line">    opt = _Options()</span><br><span class="line">    arg = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(args)):</span><br><span class="line">        a = args[i]</span><br><span class="line">        <span class="keyword">if</span> a == <span class="string">&quot;-h&quot;</span> <span class="keyword">or</span> a == <span class="string">&quot;--help&quot;</span>:</span><br><span class="line">            opt.<span class="built_in">help</span> = <span class="literal">True</span></span><br><span class="line">        <span class="keyword">elif</span> a == <span class="string">&quot;--version&quot;</span>:</span><br><span class="line">            opt.version = <span class="literal">True</span></span><br><span class="line">        <span class="keyword">elif</span> a == <span class="string">&quot;--trace&quot;</span>:</span><br><span class="line">            trace.<span class="built_in">set</span>(<span class="literal">True</span>)</span><br><span class="line">        <span class="keyword">elif</span> <span class="keyword">not</span> a.startswith(<span class="string">&quot;-&quot;</span>):</span><br><span class="line">            cmd = a</span><br><span class="line">            arg = args[i + <span class="number">1</span> :]</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    <span class="keyword">return</span> cmd, opt, arg</span><br></pre></td></tr></table></figure>

<h4 id="2-1-2-FindRepo"><a href="#2-1-2-FindRepo" class="headerlink" title="2.1.2 _FindRepo"></a><font size=3>2.1.2 _FindRepo</font></h4><p>_FindRepo() 在从当前目录开始往上遍历直到根据目录。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927063525511.png" alt="image-20240927063525511"  />

<p>如果中间某一个目录下面存在一个.repo&#x2F;repo目录，并且该.repo&#x2F;repo存在一个main.py文件，那么就会认为当前是AOSP中执行Repo脚本，这时候它就会返回文件main.py的绝对路径和当前查找目录下的.repo子目录的绝对路径给调用者。在上述情况下，实际上是说明Repo仓库已经安装过了。_FindRepo的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_FindRepo</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Look for a repo installation, starting at the current directory.&quot;&quot;&quot;</span></span><br><span class="line">    curdir = os.getcwd()</span><br><span class="line">    repo = <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    olddir = <span class="literal">None</span></span><br><span class="line">    <span class="keyword">while</span> curdir != olddir <span class="keyword">and</span> <span class="keyword">not</span> repo:</span><br><span class="line">        repo = os.path.join(curdir, repodir, REPO_MAIN)</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.isfile(repo):</span><br><span class="line">            repo = <span class="literal">None</span></span><br><span class="line">            olddir = curdir</span><br><span class="line">            curdir = os.path.dirname(curdir)</span><br><span class="line">    <span class="keyword">return</span> (repo, os.path.join(curdir, repodir))</span><br></pre></td></tr></table></figure>

<h4 id="2-1-3-RunSelf"><a href="#2-1-3-RunSelf" class="headerlink" title="2.1.3 _RunSelf()"></a><font size=3>2.1.3 _RunSelf()</font></h4><p>_RunSelf检查Repo脚本所在目录是否存在一个Repo仓库，如果存在的话，就从该仓库克隆一个新的仓库到执行Repo脚本的目录来。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927064108549.png" alt="image-20240927064108549" />

<p>实际上就是从本地克隆一个新的Repo仓库。如果不存在的话，那么就需要从远程地址克隆一个Repo仓库到本地来。这个远程地址是Hard Code在Repo脚本里面。_RunSelf的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_RunSelf</span>(<span class="params">wrapper_path</span>):</span><br><span class="line">    my_dir = os.path.dirname(wrapper_path)</span><br><span class="line">    my_main = os.path.join(my_dir, <span class="string">&quot;main.py&quot;</span>)</span><br><span class="line">    my_git = os.path.join(my_dir, <span class="string">&quot;.git&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> os.path.isfile(my_main) <span class="keyword">and</span> os.path.isdir(my_git):</span><br><span class="line">        <span class="keyword">for</span> name <span class="keyword">in</span> [<span class="string">&quot;git_config.py&quot;</span>, <span class="string">&quot;project.py&quot;</span>, <span class="string">&quot;subcmds&quot;</span>]:</span><br><span class="line">            <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(os.path.join(my_dir, name)):</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span></span><br><span class="line">        <span class="keyword">return</span> my_main, my_git</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">None</span>, <span class="literal">None</span></span><br></pre></td></tr></table></figure>

<p>从这里我们就可以看出，如果Repo脚本所在的目录存在一个Repo仓库，那么要满足以下条件：</p>
<p>（1）存在一个.git目录；</p>
<p>（2）存在一个main.py文件；</p>
<p>（3）存在一个git_config.py文件；</p>
<p>（4）存在一个project.py文件；</p>
<p>（5）存在一个subcmds目录。</p>
<p>这些目录和文件实际上都是一个标准的Repo仓库所具有的。</p>
<h4 id="2-1-4-SetDefaultsTo"><a href="#2-1-4-SetDefaultsTo" class="headerlink" title="2.1.4 _SetDefaultsTo()"></a><font size=3>2.1.4 _SetDefaultsTo()</font></h4><p>我们再回到main函数中。如果调用_FindRepo得到的repo_main的值等于空，那么就说明当前目录还没有安装Repo仓库，这时候Repo后面所跟的参数只能是help或者init，否则的话，就会显示错误信息。如果Repo后面跟的参数是help，就打印出Repo脚本的帮助文档。</p>
<p>我们关注Repo后面跟的参数是init的情况。这时候看一下调用_RunSelf的返回值my_git是否不等于空。如果不等于空的话，那么就说明Repo脚本所在目录存一个Repo仓库，这时候就调用_SetDefaultsTo设置等一会要克隆的Repo仓库源。</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927064649242.png" alt="image-20240927064649242"  />

<p>_SetDefaultsTo的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_SetDefaultsTo</span>(<span class="params">gitdir</span>):</span><br><span class="line">    <span class="keyword">global</span> REPO_URL</span><br><span class="line">    <span class="keyword">global</span> REPO_REV</span><br><span class="line"></span><br><span class="line">    REPO_URL = gitdir</span><br><span class="line">    ret = run_git(<span class="string">&quot;--git-dir=%s&quot;</span> % gitdir, <span class="string">&quot;symbolic-ref&quot;</span>, <span class="string">&quot;HEAD&quot;</span>, check=<span class="literal">False</span>)</span><br><span class="line">    <span class="keyword">if</span> ret.returncode:</span><br><span class="line">        <span class="comment"># If we&#x27;re not tracking a branch (bisect/etc...), then fall back to commit.</span></span><br><span class="line">        <span class="built_in">print</span>(</span><br><span class="line">            <span class="string">&quot;repo: warning: %s has no current branch; using HEAD&quot;</span> % gitdir,</span><br><span class="line">            file=sys.stderr,</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            ret = run_git(<span class="string">&quot;rev-parse&quot;</span>, <span class="string">&quot;HEAD&quot;</span>, cwd=gitdir)</span><br><span class="line">        <span class="keyword">except</span> CloneFailure:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;fatal: %s has invalid HEAD&quot;</span> % gitdir, file=sys.stderr)</span><br><span class="line">            sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    REPO_REV = ret.stdout.strip()</span><br></pre></td></tr></table></figure>

<p>最开始的变量是什么？我们可以搜索一下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># repo default configuration</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line">REPO_URL = os.environ.get(<span class="string">&quot;REPO_URL&quot;</span>, <span class="literal">None</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> REPO_URL:</span><br><span class="line">    REPO_URL = <span class="string">&quot;https://gerrit.googlesource.com/git-repo&quot;</span></span><br><span class="line">REPO_REV = os.environ.get(<span class="string">&quot;REPO_REV&quot;</span>)</span><br><span class="line"><span class="keyword">if</span> <span class="keyword">not</span> REPO_REV:</span><br><span class="line">    REPO_REV = <span class="string">&quot;stable&quot;</span></span><br></pre></td></tr></table></figure>

<p>如果Repo脚本所在目录不存在一个Repo仓库，那么默认就将<a target="_blank" rel="noopener" href="https://gerrit.googlesource.com/git-repo">https://gerrit.googlesource.com/git-repo</a>设置为一会要克隆的Repo仓库源，并且要克隆的分支是stable。否则的话，就以该Repo仓库作为克隆源，并且以该Repo仓库的当前分支作为要克隆的分支。</p>
<p>从前面的分析就可以知道，当我们执行”repo init”命令的时候：</p>
<p>（1）如果我们只是从网上下载了一个Repo脚本，那么在执行Repo命令的时候，就会从远程克隆一个Repo仓库到当前执行Repo脚本的目录来。</p>
<p>（2）如果我们从网上下载的是一个带有Repo仓库的Repo脚本，那么在执行Repo命令的时候，就可以从本地克隆一个Repo仓库到当前执行Repo脚本的目录来。</p>
<h4 id="2-1-5-Init"><a href="#2-1-5-Init" class="headerlink" title="2.1.5 _Init()"></a><font size=3>2.1.5 _Init()</font></h4><p>我们再继续看main函数的实现，它接下来调用_Init在当前执行Repo脚本的目录下安装一个Repo仓库：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240927070242090.png" alt="image-20240927070242090" />

<p>这个函数的实现如下：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_Init</span>(<span class="params">args, gitc_init=<span class="literal">False</span></span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Installs repo by cloning it over the network.&quot;&quot;&quot;</span></span><br><span class="line">    parser = GetParser(gitc_init=gitc_init)</span><br><span class="line">    opt, args = parser.parse_args(args)</span><br><span class="line">    <span class="keyword">if</span> args:</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> opt.manifest_url:</span><br><span class="line">            opt.manifest_url = args.pop(<span class="number">0</span>)</span><br><span class="line">        <span class="keyword">if</span> args:</span><br><span class="line">            parser.print_usage()</span><br><span class="line">            sys.exit(<span class="number">1</span>)</span><br><span class="line">    opt.quiet = opt.output_mode <span class="keyword">is</span> <span class="literal">False</span></span><br><span class="line">    opt.verbose = opt.output_mode <span class="keyword">is</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> opt.clone_bundle <span class="keyword">is</span> <span class="literal">None</span>:</span><br><span class="line">        opt.clone_bundle = <span class="literal">False</span> <span class="keyword">if</span> opt.partial_clone <span class="keyword">else</span> <span class="literal">True</span></span><br><span class="line"></span><br><span class="line">    url = opt.repo_url <span class="keyword">or</span> REPO_URL</span><br><span class="line">    rev = opt.repo_rev <span class="keyword">or</span> REPO_REV</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        os.mkdir(repodir)</span><br><span class="line">    <span class="keyword">except</span> OSError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="keyword">if</span> e.errno != errno.EEXIST:</span><br><span class="line">            <span class="built_in">print</span>(</span><br><span class="line">                <span class="string">f&quot;fatal: cannot make <span class="subst">&#123;repodir&#125;</span> directory: <span class="subst">&#123;e.strerror&#125;</span>&quot;</span>,</span><br><span class="line">                file=sys.stderr,</span><br><span class="line">            )</span><br><span class="line">            <span class="comment"># Don&#x27;t raise CloneFailure; that would delete the</span></span><br><span class="line">            <span class="comment"># name. Instead exit immediately.</span></span><br><span class="line">            <span class="comment">#</span></span><br><span class="line">            sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    _CheckGitVersion()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> opt.quiet:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;Downloading Repo source from&quot;</span>, url)</span><br><span class="line">        dst_final = os.path.abspath(os.path.join(repodir, S_repo))</span><br><span class="line">        dst = dst_final + <span class="string">&quot;.tmp&quot;</span></span><br><span class="line">        shutil.rmtree(dst, ignore_errors=<span class="literal">True</span>)</span><br><span class="line">        _Clone(url, dst, opt.clone_bundle, opt.quiet, opt.verbose)</span><br><span class="line"></span><br><span class="line">        remote_ref, rev = check_repo_rev(</span><br><span class="line">            dst, rev, opt.repo_verify, quiet=opt.quiet</span><br><span class="line">        )</span><br><span class="line">        _Checkout(dst, remote_ref, rev, opt.quiet)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> <span class="keyword">not</span> os.path.isfile(os.path.join(dst, <span class="string">&quot;repo&quot;</span>)):</span><br><span class="line">            <span class="built_in">print</span>(</span><br><span class="line">                <span class="string">&quot;fatal: &#x27;%s&#x27; does not look like a git-repo repository, is &quot;</span></span><br><span class="line">                <span class="string">&quot;--repo-url set correctly?&quot;</span> % url,</span><br><span class="line">                file=sys.stderr,</span><br><span class="line">            )</span><br><span class="line">            <span class="keyword">raise</span> CloneFailure()</span><br><span class="line"></span><br><span class="line">        os.rename(dst, dst_final)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">except</span> CloneFailure:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;fatal: double check your --repo-rev setting.&quot;</span>, file=sys.stderr)</span><br><span class="line">        <span class="keyword">if</span> opt.quiet:</span><br><span class="line">            <span class="built_in">print</span>(</span><br><span class="line">                <span class="string">&quot;fatal: repo init failed; run without --quiet to see why&quot;</span>,</span><br><span class="line">                file=sys.stderr,</span><br><span class="line">            )</span><br><span class="line">        <span class="keyword">raise</span></span><br></pre></td></tr></table></figure>

<p>如果我们在执行Repo脚本的时候，没有通过–repo-url和–repo-branch来指定Repo仓库的源地址和分支，那么就使用由REPO_URL和REPO_REV所指定的源地址和分支。从前面的分析可以知道，REPO_URL和REPO_REV要么指向远程地址<a target="_blank" rel="noopener" href="https://gerrit.googlesource.com/git-repo">https://gerrit.googlesource.com/git-repo</a>和分支stable，要么指向Repo脚本所在目录的Repo仓库和该仓库的当前分支。</p>
<p>_Init函数继续检查当前执行Repo脚本的目录是否存在一个.repo目录。如果不存在的话，那么就新创建一个。接着是否需要验证等一会克隆回来的Repo仓库的GPG。如果需要验证的话，那么就会在调用_Clone函数来克隆好Repo仓库之后，调用_Verify函数来验证该Repo仓库的GPG。</p>
<p>最关键的地方就在于函数_Clone函数和_Checkout函数的调用，前者用来从url指定的地方克隆一个仓库到dst指定的地方来，实际上就是克隆到当前目录下的.&#x2F;repo&#x2F;repo目录来，后者在克隆回来的仓库中将branch分支checkout出来。</p>
<p>函数_Clone的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_Clone</span>(<span class="params">url, cwd, clone_bundle, quiet, verbose</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Clones a git repository to a new subdirectory of repodir&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">if</span> verbose:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;Cloning git repository&quot;</span>, url)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        os.mkdir(cwd)</span><br><span class="line">    <span class="keyword">except</span> OSError <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span>(</span><br><span class="line">            <span class="string">f&quot;fatal: cannot make <span class="subst">&#123;cwd&#125;</span> directory: <span class="subst">&#123;e.strerror&#125;</span>&quot;</span>,</span><br><span class="line">            file=sys.stderr,</span><br><span class="line">        )</span><br><span class="line">        <span class="keyword">raise</span> CloneFailure()</span><br><span class="line"></span><br><span class="line">    run_git(<span class="string">&quot;init&quot;</span>, <span class="string">&quot;--quiet&quot;</span>, cwd=cwd)</span><br><span class="line"></span><br><span class="line">    _InitHttp()</span><br><span class="line">    _SetConfig(cwd, <span class="string">&quot;remote.origin.url&quot;</span>, url)</span><br><span class="line">    _SetConfig(</span><br><span class="line">        cwd, <span class="string">&quot;remote.origin.fetch&quot;</span>, <span class="string">&quot;+refs/heads/*:refs/remotes/origin/*&quot;</span></span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">if</span> clone_bundle <span class="keyword">and</span> _DownloadBundle(url, cwd, quiet, verbose):</span><br><span class="line">        _ImportBundle(cwd)</span><br><span class="line">    _Fetch(url, cwd, <span class="string">&quot;origin&quot;</span>, quiet, verbose)</span><br></pre></td></tr></table></figure>

<p>这个函数首先是调用”git init”在当前目录下的.repo&#x2F;repo子目录初始化一个Git仓库，接着再调用_SetConfig函来设置该Git仓库的url信息等。再接着调用_DownloadBundle来检查指定的url是否存在一个clone.bundle文件。如果存在这个clone.bundle文件的话，就以它作为Repo仓库的克隆源。</p>
<p>函数_DownloadBundle的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_DownloadBundle</span>(<span class="params">url, cwd, quiet, verbose</span>):</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> url.endswith(<span class="string">&quot;/&quot;</span>):</span><br><span class="line">        url += <span class="string">&quot;/&quot;</span></span><br><span class="line">    url += <span class="string">&quot;clone.bundle&quot;</span></span><br><span class="line"></span><br><span class="line">    ret = run_git(</span><br><span class="line">        <span class="string">&quot;config&quot;</span>, <span class="string">&quot;--get-regexp&quot;</span>, <span class="string">&quot;url.*.insteadof&quot;</span>, cwd=cwd, check=<span class="literal">False</span></span><br><span class="line">    )</span><br><span class="line">    <span class="keyword">for</span> line <span class="keyword">in</span> ret.stdout.splitlines():</span><br><span class="line">        m = re.<span class="built_in">compile</span>(<span class="string">r&quot;^url\.(.*)\.insteadof (.*)$&quot;</span>).<span class="keyword">match</span>(line)</span><br><span class="line">        <span class="keyword">if</span> m:</span><br><span class="line">            new_url = m.group(<span class="number">1</span>)</span><br><span class="line">            old_url = m.group(<span class="number">2</span>)</span><br><span class="line">            <span class="keyword">if</span> url.startswith(old_url):</span><br><span class="line">                url = new_url + url[<span class="built_in">len</span>(old_url) :]</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> url.startswith(<span class="string">&quot;http:&quot;</span>) <span class="keyword">and</span> <span class="keyword">not</span> url.startswith(<span class="string">&quot;https:&quot;</span>):</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line">    dest = <span class="built_in">open</span>(os.path.join(cwd, <span class="string">&quot;.git&quot;</span>, <span class="string">&quot;clone.bundle&quot;</span>), <span class="string">&quot;w+b&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            r = urllib.request.urlopen(url)</span><br><span class="line">        <span class="keyword">except</span> urllib.error.HTTPError <span class="keyword">as</span> e:</span><br><span class="line">            <span class="keyword">if</span> e.code <span class="keyword">not</span> <span class="keyword">in</span> [<span class="number">400</span>, <span class="number">401</span>, <span class="number">403</span>, <span class="number">404</span>, <span class="number">501</span>]:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&quot;warning: Cannot get %s&quot;</span> % url, file=sys.stderr)</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&quot;warning: HTTP error %s&quot;</span> % e.code, file=sys.stderr)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line">        <span class="keyword">except</span> urllib.error.URLError <span class="keyword">as</span> e:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;fatal: Cannot get %s&quot;</span> % url, file=sys.stderr)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&quot;fatal: error %s&quot;</span> % e.reason, file=sys.stderr)</span><br><span class="line">            <span class="keyword">raise</span> CloneFailure()</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            <span class="keyword">if</span> verbose:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&quot;Downloading clone bundle %s&quot;</span> % url, file=sys.stderr)</span><br><span class="line">            <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">                buf = r.read(<span class="number">8192</span>)</span><br><span class="line">                <span class="keyword">if</span> <span class="keyword">not</span> buf:</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">                dest.write(buf)</span><br><span class="line">        <span class="keyword">finally</span>:</span><br><span class="line">            r.close()</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        dest.close()</span><br></pre></td></tr></table></figure>

<p>Bundle文件是Git提供的一种机制，用来解决不能正常通过git、ssh和http等网络协议从远程地址克隆Git仓库的问题。简单来说，就是我们可以用“git bundle”命令来在一个Git仓库创建一个Bundle文件，这个Bundle文件就会包含Git仓库的提交历史。把这个Bundle文件通过其它方式拷贝到另一台机器上，就可以将它作为一个本地Git仓库来使用，而不用去访问远程网络。</p>
<p>回到函数_Clone中，如果存在对应的clone.bundle文件，并且能成功下载到该clone.bundle，接下来就调用函数_ImportBundle将它作为源仓库克隆为新的Repo仓库。函数_ImportBundle的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_ImportBundle</span>(<span class="params">cwd</span>):</span><br><span class="line">    path = os.path.join(cwd, <span class="string">&quot;.git&quot;</span>, <span class="string">&quot;clone.bundle&quot;</span>)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        _Fetch(cwd, cwd, path, <span class="literal">True</span>, <span class="literal">False</span>)</span><br><span class="line">    <span class="keyword">finally</span>:</span><br><span class="line">        os.remove(path)</span><br></pre></td></tr></table></figure>

<p>结合_Clone函数和_ImportBundle函数就可以看出，从clone.bundle文件克隆Repo仓库和从远程url克隆Repo仓库都是通过函数_Fetch来实现的。区别就在于调用函数_Fetch时指定的第三个参数，前者是已经下载到本地的一个clone.bundle文件路径，后者是origin（表示远程仓库名称）。函数_Fetch的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_Fetch</span>(<span class="params">url, cwd, src, quiet, verbose</span>):</span><br><span class="line">    cmd = [<span class="string">&quot;fetch&quot;</span>]</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> verbose:</span><br><span class="line">        cmd.append(<span class="string">&quot;--quiet&quot;</span>)</span><br><span class="line">    err = <span class="literal">None</span></span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> quiet <span class="keyword">and</span> sys.stdout.isatty():</span><br><span class="line">        cmd.append(<span class="string">&quot;--progress&quot;</span>)</span><br><span class="line">    <span class="keyword">elif</span> <span class="keyword">not</span> verbose:</span><br><span class="line">        err = subprocess.PIPE</span><br><span class="line">    cmd.append(src)</span><br><span class="line">    cmd.append(<span class="string">&quot;+refs/heads/*:refs/remotes/origin/*&quot;</span>)</span><br><span class="line">    cmd.append(<span class="string">&quot;+refs/tags/*:refs/tags/*&quot;</span>)</span><br><span class="line">    run_git(*cmd, stderr=err, capture_output=<span class="literal">False</span>, cwd=cwd)</span><br></pre></td></tr></table></figure>

<p>函数_Fetch实际上就是通过“git fetch”从指定的仓库源克隆一个新的Repo仓库到当前目录下的.repo&#x2F;repo子目录来。</p>
<p>注意，以上只是克隆好了一个Repo仓库，接下来还需要从这个Repo仓库checkout出一个分支来，才能正常工作。从Repo仓库checkout出一个分支是通过调用函数_Checkout来实现的：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_Checkout</span>(<span class="params">cwd, remote_ref, rev, quiet</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;Checkout an upstream branch into the repository and track it.&quot;&quot;&quot;</span></span><br><span class="line">    run_git(<span class="string">&quot;update-ref&quot;</span>, <span class="string">&quot;refs/heads/default&quot;</span>, rev, cwd=cwd)</span><br><span class="line"></span><br><span class="line">    _SetConfig(cwd, <span class="string">&quot;branch.default.remote&quot;</span>, <span class="string">&quot;origin&quot;</span>)</span><br><span class="line">    _SetConfig(cwd, <span class="string">&quot;branch.default.merge&quot;</span>, remote_ref)</span><br><span class="line"></span><br><span class="line">    run_git(<span class="string">&quot;symbolic-ref&quot;</span>, <span class="string">&quot;HEAD&quot;</span>, <span class="string">&quot;refs/heads/default&quot;</span>, cwd=cwd)</span><br><span class="line"></span><br><span class="line">    cmd = [<span class="string">&quot;read-tree&quot;</span>, <span class="string">&quot;--reset&quot;</span>, <span class="string">&quot;-u&quot;</span>]</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> quiet:</span><br><span class="line">        cmd.append(<span class="string">&quot;-v&quot;</span>)</span><br><span class="line">    cmd.append(<span class="string">&quot;HEAD&quot;</span>)</span><br><span class="line">    run_git(*cmd, cwd=cwd)</span><br></pre></td></tr></table></figure>

<p>要checkout出来的分支由参数branch指定。从前面的分析可以知道，如果当前执行的Repo脚本所在目录存在一个Repo仓库，那么参数branch描述的就是该仓库当前checkout出来的分支。否则的话，参数branch描述的就是从远程仓库克隆回来的“stable”分支。</p>
<p>需要注意的是，这里从仓库checkout出分支不是使用“git checkout”命令来实现的，而是通过更底层的Git命令“git update-ref”来实现的。实际上，“git checkout”命令也是通过“git update-ref”命令来实现的，只不过它进行了更高层的封装，更方便使用。如果我们去分析组成Repo仓库的那些Python脚本命令，就会发现它们基本上都是通过底层的Git命令来完成Git功能的。</p>
<h3 id="2-3-Manifest仓库"><a href="#2-3-Manifest仓库" class="headerlink" title="2.3 Manifest仓库"></a><font size=3>2.3 Manifest仓库</font></h3><blockquote>
<p>【注意】这一部分我就不是按自己下载的最新的repo仓库源码分析的了，直接复制的原文：<a target="_blank" rel="noopener" href="https://www.jb51.net/article/131935.htm">Android源代码仓库及其管理工具Repo分析详解_Android_脚本之家 (jb51.net)</a>，主要是这里没看懂。哈哈。</p>
</blockquote>
<p>我们接着再分析下面这个命令的执行：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo init -u https://android.googlesource.com/platform/manifest</span><br></pre></td></tr></table></figure>

<p>如前所述，这个命令安装好Repo仓库之后，就会调用该Repo仓库下面的main.py脚本，对应的文件为.repo&#x2F;repo&#x2F;main.py，它的入口函数的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">def</span> <span class="title function_">_Main</span>(<span class="params">argv</span>): </span><br><span class="line"> result = <span class="number">0</span> </span><br><span class="line"> </span><br><span class="line"> opt = optparse.OptionParser(usage=<span class="string">&quot;repo wrapperinfo -- ...&quot;</span>) </span><br><span class="line"> opt.add_option(<span class="string">&quot;--repo-dir&quot;</span>, dest=<span class="string">&quot;repodir&quot;</span>, </span><br><span class="line">         <span class="built_in">help</span>=<span class="string">&quot;path to .repo/&quot;</span>) </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> repo = _Repo(opt.repodir) </span><br><span class="line"> <span class="keyword">try</span>: </span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   init_ssh() </span><br><span class="line">   init_http() </span><br><span class="line">   result = repo._Run(argv) <span class="keyword">or</span> <span class="number">0</span> </span><br><span class="line">  <span class="keyword">finally</span>: </span><br><span class="line">   close_ssh() </span><br><span class="line"> <span class="keyword">except</span> KeyboardInterrupt: </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line">  result = <span class="number">1</span> </span><br><span class="line"> <span class="keyword">except</span> ManifestParseError <span class="keyword">as</span> mpe: </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line">  result = <span class="number">1</span> </span><br><span class="line"> <span class="keyword">except</span> RepoChangedException <span class="keyword">as</span> rce: </span><br><span class="line">  <span class="comment"># If repo changed, re-exec ourselves. </span></span><br><span class="line">  <span class="comment"># </span></span><br><span class="line">  argv = <span class="built_in">list</span>(sys.argv) </span><br><span class="line">  argv.extend(rce.extra_args) </span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   os.execv(__file__, argv) </span><br><span class="line">  <span class="keyword">except</span> OSError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   result = <span class="number">128</span> </span><br><span class="line"> </span><br><span class="line"> sys.exit(result) </span><br><span class="line"> </span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&#x27;__main__&#x27;</span>: </span><br><span class="line"> _Main(sys.argv[<span class="number">1</span>:]) </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>从前面的分析可以知道，通过参数–repo-dir传进来的是AOSP根目录下的.repo目录，这是一个隐藏目录，里面保存的是Repo仓库、Manifest仓库，以及各个AOSP子项目仓库。函数_Main会调用前面创建的一个_Repo对象的成员函数_Run来解析要执行的命令，并且执行这个命令。_Repo类的成员函数_Run的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> subcmds <span class="keyword">import</span> all_commands </span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">_Repo</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, repodir</span>): </span><br><span class="line">  self.repodir = repodir </span><br><span class="line">  self.commands = all_commands </span><br><span class="line">  <span class="comment"># add &#x27;branch&#x27; as an alias for &#x27;branches&#x27; </span></span><br><span class="line">  all_commands[<span class="string">&#x27;branch&#x27;</span>] = all_commands[<span class="string">&#x27;branches&#x27;</span>] </span><br><span class="line">  <span class="keyword">def</span> <span class="title function_">_Run</span>(<span class="params">self, argv</span>): </span><br><span class="line">  result = <span class="number">0</span> </span><br><span class="line">  name = <span class="literal">None</span> </span><br><span class="line">  glob = [] </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="built_in">len</span>(argv)): </span><br><span class="line">   <span class="keyword">if</span> <span class="keyword">not</span> argv[i].startswith(<span class="string">&#x27;-&#x27;</span>): </span><br><span class="line">    name = argv[i] </span><br><span class="line">    <span class="keyword">if</span> i &gt; <span class="number">0</span>: </span><br><span class="line">     glob = argv[:i] </span><br><span class="line">    argv = argv[i + <span class="number">1</span>:] </span><br><span class="line">    <span class="keyword">break</span> </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> name: </span><br><span class="line">   glob = argv </span><br><span class="line">   name = <span class="string">&#x27;help&#x27;</span> </span><br><span class="line">   argv = [] </span><br><span class="line">  gopts, _gargs = global_options.parse_args(glob)  </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   cmd = self.commands[name] </span><br><span class="line">  <span class="keyword">except</span> KeyError: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   <span class="keyword">return</span> <span class="number">1</span>  </span><br><span class="line">  cmd.repodir = self.repodir </span><br><span class="line">  cmd.manifest = XmlManifest(cmd.repodir)  </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   result = cmd.Execute(copts, cargs) </span><br><span class="line">  <span class="keyword">except</span> DownloadError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   result = <span class="number">1</span> </span><br><span class="line">  <span class="keyword">except</span> ManifestInvalidRevisionError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   result = <span class="number">1</span> </span><br><span class="line">  <span class="keyword">except</span> NoManifestException <span class="keyword">as</span> e: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   result = <span class="number">1</span> </span><br><span class="line">  <span class="keyword">except</span> NoSuchProjectError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   result = <span class="number">1</span> </span><br><span class="line">  <span class="keyword">finally</span>: </span><br><span class="line">   <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">return</span> result </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Repo脚本能执行的命令都放在目录.repo&#x2F;repo&#x2F;subcmds中，该目录每一个python文件都对应一个Repo命令。例如，“repo init”表示要执行命令脚本是.repo&#x2F;repo&#x2F;subcmds&#x2F;init.py。</p>
<p>_Repo类的成员函数_Run首先是在repo后面所带的参数中，不是以横线“-”开始的第一个选项，该选项就代表要执行的命令，该命令的名称就保存在变量name中。接着根据变量name的值在_Repo类的成员变量commands中找到对应的命令模块cmd，并且指定该命令模块cmd的成员变量repodir和manifest的值。命令模块cmd的成员变量repodir描述的就是AOSP的.repo目录，成员变量manifest指向的是一个XmlManifest对象，它描述的是AOSP的Repo仓库和Manifest仓库。</p>
<p>我们看看XmlManifest类的构造函数，它定义在文件.repo&#x2F;repo&#x2F;xml_manifest.py文件中：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">XmlManifest</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="string">&quot;&quot;&quot;manages the repo configuration file&quot;&quot;&quot;</span>  </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, repodir</span>): </span><br><span class="line">  self.repodir = os.path.abspath(repodir) </span><br><span class="line">  self.topdir = os.path.dirname(self.repodir) </span><br><span class="line">  self.manifestFile = os.path.join(self.repodir, MANIFEST_FILE_NAME) </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  self.repoProject = MetaProject(self, <span class="string">&#x27;repo&#x27;</span>, </span><br><span class="line">   gitdir  = os.path.join(repodir, <span class="string">&#x27;repo/.git&#x27;</span>), </span><br><span class="line">   worktree = os.path.join(repodir, <span class="string">&#x27;repo&#x27;</span>))  </span><br><span class="line">  self.manifestProject = MetaProject(self, <span class="string">&#x27;manifests&#x27;</span>, </span><br><span class="line">   gitdir  = os.path.join(repodir, <span class="string">&#x27;manifests.git&#x27;</span>), </span><br><span class="line">   worktree = os.path.join(repodir, <span class="string">&#x27;manifests&#x27;</span>))  </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>XmlManifest作了描述了AOSP的Repo目录（repodir）、AOSP 根目录（topdir）和Manifest.xml文件（manifestFile）之外，还使用两个MetaProject对象描述了AOSP的Repo仓库（repoProject）和Manifest仓库（manifestProject）。</p>
<p>在AOSP中，每一个子项目（或者说仓库）都用一个Project对象来描述。Project类定义在文件.repo&#x2F;repo&#x2F;project.py文件中，用来封装对各个项目的基础Git操作，例如，对项目进行暂存、提交和更新等。它的构造函数如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, </span></span><br><span class="line"><span class="params">        manifest, </span></span><br><span class="line"><span class="params">        name, </span></span><br><span class="line"><span class="params">        remote, </span></span><br><span class="line"><span class="params">        gitdir, </span></span><br><span class="line"><span class="params">        worktree, </span></span><br><span class="line"><span class="params">        relpath, </span></span><br><span class="line"><span class="params">        revisionExpr, </span></span><br><span class="line"><span class="params">        revisionId, </span></span><br><span class="line"><span class="params">        rebase = <span class="literal">True</span>, </span></span><br><span class="line"><span class="params">        groups = <span class="literal">None</span>, </span></span><br><span class="line"><span class="params">        sync_c = <span class="literal">False</span>, </span></span><br><span class="line"><span class="params">        sync_s = <span class="literal">False</span>, </span></span><br><span class="line"><span class="params">        clone_depth = <span class="literal">None</span>, </span></span><br><span class="line"><span class="params">        upstream = <span class="literal">None</span>, </span></span><br><span class="line"><span class="params">        parent = <span class="literal">None</span>, </span></span><br><span class="line"><span class="params">        is_derived = <span class="literal">False</span>, </span></span><br><span class="line"><span class="params">        dest_branch = <span class="literal">None</span></span>): </span><br><span class="line">  <span class="string">&quot;&quot;&quot;Init a Project object. </span></span><br><span class="line"><span class="string">  Args: </span></span><br><span class="line"><span class="string">   manifest: The XmlManifest object. </span></span><br><span class="line"><span class="string">   name: The `name` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   remote: RemoteSpec object specifying its remote&#x27;s properties. </span></span><br><span class="line"><span class="string">   gitdir: Absolute path of git directory. </span></span><br><span class="line"><span class="string">   worktree: Absolute path of git working tree. </span></span><br><span class="line"><span class="string">   relpath: Relative path of git working tree to repo&#x27;s top directory. </span></span><br><span class="line"><span class="string">   revisionExpr: The `revision` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   revisionId: git commit id for checking out. </span></span><br><span class="line"><span class="string">   rebase: The `rebase` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   groups: The `groups` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   sync_c: The `sync-c` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   sync_s: The `sync-s` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   upstream: The `upstream` attribute of manifest.xml&#x27;s project element. </span></span><br><span class="line"><span class="string">   parent: The parent Project object. </span></span><br><span class="line"><span class="string">   is_derived: False if the project was explicitly defined in the manifest; </span></span><br><span class="line"><span class="string">         True if the project is a discovered submodule. </span></span><br><span class="line"><span class="string">   dest_branch: The branch to which to push changes for review by default. </span></span><br><span class="line"><span class="string">  &quot;&quot;&quot;</span> </span><br><span class="line">  self.manifest = manifest </span><br><span class="line">  self.name = name </span><br><span class="line">  self.remote = remote </span><br><span class="line">  self.gitdir = gitdir.replace(<span class="string">&#x27;\\&#x27;</span>, <span class="string">&#x27;/&#x27;</span>) </span><br><span class="line">  <span class="keyword">if</span> worktree: </span><br><span class="line">   self.worktree = worktree.replace(<span class="string">&#x27;\\&#x27;</span>, <span class="string">&#x27;/&#x27;</span>) </span><br><span class="line">  <span class="keyword">else</span>: </span><br><span class="line">   self.worktree = <span class="literal">None</span> </span><br><span class="line">  self.relpath = relpath </span><br><span class="line">  self.revisionExpr = revisionExpr </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span>  revisionId <span class="keyword">is</span> <span class="literal">None</span> \ </span><br><span class="line">   <span class="keyword">and</span> revisionExpr \ </span><br><span class="line">   <span class="keyword">and</span> IsId(revisionExpr): </span><br><span class="line">   self.revisionId = revisionExpr </span><br><span class="line">  <span class="keyword">else</span>: </span><br><span class="line">   self.revisionId = revisionId </span><br><span class="line"> </span><br><span class="line">  self.rebase = rebase </span><br><span class="line">  self.groups = groups </span><br><span class="line">  self.sync_c = sync_c </span><br><span class="line">  self.sync_s = sync_s </span><br><span class="line">  self.clone_depth = clone_depth </span><br><span class="line">  self.upstream = upstream </span><br><span class="line">  self.parent = parent </span><br><span class="line">  self.is_derived = is_derived </span><br><span class="line">  self.subprojects = [] </span><br><span class="line"> </span><br><span class="line">  self.snapshots = &#123;&#125; </span><br><span class="line">  self.copyfiles = [] </span><br><span class="line">  self.annotations = [] </span><br><span class="line">  self.config = GitConfig.ForRepository( </span><br><span class="line">          gitdir = self.gitdir, </span><br><span class="line">          defaults = self.manifest.globalConfig) </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> self.worktree: </span><br><span class="line">   self.work_git = self._GitGetByExec(self, bare=<span class="literal">False</span>) </span><br><span class="line">  <span class="keyword">else</span>: </span><br><span class="line">   self.work_git = <span class="literal">None</span> </span><br><span class="line">  self.bare_git = self._GitGetByExec(self, bare=<span class="literal">True</span>) </span><br><span class="line">  self.bare_ref = GitRefs(gitdir) </span><br><span class="line">  self.dest_branch = dest_branch </span><br><span class="line"> </span><br><span class="line">  <span class="comment"># This will be filled in if a project is later identified to be the </span></span><br><span class="line">  <span class="comment"># project containing repo hooks. </span></span><br><span class="line">  self.enabled_repo_hooks = [] </span><br></pre></td></tr></table></figure>

<p>Project类构造函数的各个参数的含义见注释，这里为了方便描述，用中文描述一下：</p>
<p>manifest：指向一个XmlManifest对象，描述AOSP的Repo仓库和Manifest仓库元信息。</p>
<p>name：项目名称。</p>
<p>remote：描述项目对应的远程仓库元信息。</p>
<p>gitdir：项目的Git仓库目录。</p>
<p>worktree：项目的工作目录。</p>
<p>relpath：项目的相对于AOSP根目录的工作目录。</p>
<p>revisionExpr、revisionId、rebase、groups、sync_c、sync_s和upstream：每一个项目在.repo&#x2F;repo&#x2F;manifest.xml文件中都有对应的描述，这几个属性的值就来自于该manifest.xml文件对自己的描述的，它们的含义可以参考.repo&#x2F;repo&#x2F;docs&#x2F;manifest-format.txt文件。</p>
<p>parent：父项目</p>
<p>is_derived：如果一个项目含有子模块（也是一个Git仓库），那么这些子模块也会用一个Project对象来描述，这些Project的is_derived属性会设置为true。</p>
<p>dest_branch：用来code review的分支。</p>
<blockquote>
<p>这里重点再了解下项目的Git仓库目录和工作目录的概念。一般来说，一个项目的Git仓库目录（默认为.git目录）是位于工作目录下面的，但是Git支持将一个项目的Git仓库目录和工作目录分开来存放。在AOSP中，Repo仓库的Git目录（.git）位于工作目录（.repo&#x2F;repo）下，Manifest仓库的Git目录有两份拷贝，一份（.git）位于工作目录（.repo&#x2F;manifests）下，另外一份位于 .repo&#x2F;manifests.git 目录，其余的AOSP子项目的工作目录和Git目录都是分开存放的，其中，工作目录位于AOSP根目录下，Git目录位于.repo&#x2F;repo&#x2F;projects目录下。</p>
<p>此外，每一个AOSP子项目的工作目录也有一个.git目录，不过这个.git目录是一个符号链接，链接到.repo&#x2F;repo&#x2F;projects对应的Git目录。这样，我们就既可以在AOSP子项目的工作目录下执行Git命令，也可以在其对应的Git目录下执行Git命令。一般来说，要访问到工作目录的命令（例如git status）需要在工作目录下执行，而不需要访问工作目录（例如git log）可以在Git目录下执行。</p>
<p>Project类有两个成员变量work_git和bare_git，它们指向的都是一个_GitGetByExec对象。用来封装对Git命令的执行。其中，前者在执行Git命令的时候，会将当前目录设置为项目的工作目录，而后者在执行的时候，不会设置当前目录，但是会将环境变量GIT_DIR的值设置为项目的Git目录，也就是.repo&#x2F;projects目录下面的那些目录。通过这种方式，Project类就可以根据需要来在工作目录或者Git目录下执行Git命令。</p>
</blockquote>
<p>回到XmlManifest类的构造函数中，由于Repo和Manifest也是属于Git仓库，所以我们也需要创建一个Project对象来描述它们。不过，由于它们是比较特殊的Git仓库（用来描述AOSP子项目元信息的Git仓库），所以我们就使用另外一个类型为MetaProject的对象来描述它们。MetaProject类是从Project类继承下来的，定义在project.py文件中，如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MetaProject</span>(<span class="title class_ inherited__">Project</span>): </span><br><span class="line"> <span class="string">&quot;&quot;&quot;A special project housed under .repo. </span></span><br><span class="line"><span class="string"> &quot;&quot;&quot;</span> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">__init__</span>(<span class="params">self, manifest, name, gitdir, worktree</span>): </span><br><span class="line">  Project.__init__(self, </span><br><span class="line">           manifest = manifest, </span><br><span class="line">           name = name, </span><br><span class="line">           gitdir = gitdir, </span><br><span class="line">           worktree = worktree, </span><br><span class="line">           remote = RemoteSpec(<span class="string">&#x27;origin&#x27;</span>), </span><br><span class="line">           relpath = <span class="string">&#x27;.repo/%s&#x27;</span> % name, </span><br><span class="line">           revisionExpr = <span class="string">&#x27;refs/heads/master&#x27;</span>, </span><br><span class="line">           revisionId = <span class="literal">None</span>, </span><br><span class="line">           groups = <span class="literal">None</span>) </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>既然MetaProject类是从Project类继承下来的，那么它们的Git操作几乎都可以通过Project类来完成的。实际上，MetaProject类和Project类目前的区别不是太大，可以认为是基本相同的。使用MetaProject类来描述Repo仓库和Manifest仓库，主要是为了强调它们是用来描述AOSP子项目仓库的元信息的。</p>
<p>回到_Repo类的成员函数_Run中，创建好用来描述Repo仓库和Manifest仓库的XmlManifest对象之后，就开始执行跟在repo脚本后面的不带横线<code>-</code>的选项所表示的命令。在我们这个场景中，这个命令就是init，它对应的Python模块为.repo&#x2F;repo&#x2F;subcmds&#x2F;init.py，入口函数为定义在该模块的Init类的成员函数Execute，它的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Init</span>(InteractiveCommand, MirrorSafeCommand): </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Execute</span>(<span class="params">self, opt, args</span>): </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  self._SyncManifest(opt) </span><br><span class="line">  self._LinkManifest(opt.manifest_name)  </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Init类的成员函数Execute主要就是调用另外两个成员函数_SyncManifest和_LinkManifest来完成克隆Manifest仓库的工作。Init类的成员函数_SyncManifest的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Init</span>(InteractiveCommand, MirrorSafeCommand): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line">  <span class="keyword">def</span> <span class="title function_">_SyncManifest</span>(<span class="params">self, opt</span>): </span><br><span class="line">  m = self.manifest.manifestProject </span><br><span class="line">  is_new = <span class="keyword">not</span> m.Exists </span><br><span class="line">   <span class="keyword">if</span> is_new: </span><br><span class="line">    <span class="comment">#......  </span></span><br><span class="line">   m._InitGitDir(mirror_git=mirrored_manifest_git) </span><br><span class="line"> </span><br><span class="line">   <span class="keyword">if</span> opt.manifest_branch: </span><br><span class="line">    m.revisionExpr = opt.manifest_branch </span><br><span class="line">   <span class="keyword">else</span>: </span><br><span class="line">    m.revisionExpr = <span class="string">&#x27;refs/heads/master </span></span><br><span class="line"><span class="string">  else: </span></span><br><span class="line"><span class="string">   if opt.manifest_branch: </span></span><br><span class="line"><span class="string">    m.revisionExpr = opt.manifest_branch </span></span><br><span class="line"><span class="string">   else: </span></span><br><span class="line"><span class="string">    m.PreSync() </span></span><br><span class="line"><span class="string"> </span></span><br><span class="line"><span class="string">  #...... </span></span><br><span class="line"><span class="string">   if not m.Sync_NetworkHalf(is_new=is_new): </span></span><br><span class="line"><span class="string">   #...... </span></span><br><span class="line"><span class="string">   sys.exit(1)  </span></span><br><span class="line"><span class="string">  if opt.manifest_branch: </span></span><br><span class="line"><span class="string">   m.MetaBranchSwitch(opt.manifest_branch) </span></span><br><span class="line"><span class="string">  #......  </span></span><br><span class="line"><span class="string">  m.Sync_LocalHalf(syncbuf) </span></span><br><span class="line"><span class="string">  #......  </span></span><br><span class="line"><span class="string">  if is_new or m.CurrentBranch is None: </span></span><br><span class="line"><span class="string">   if not m.StartBranch(&#x27;</span>default<span class="string">&#x27;): </span></span><br><span class="line"><span class="string">    #...... </span></span><br><span class="line"><span class="string">    sys.exit(1) </span></span><br><span class="line"><span class="string"></span></span><br></pre></td></tr></table></figure>

<p> <strong>Init类的成员函数_SyncManifest执行以下操作：</strong></p>
<p>（1）检查本地是否存在Manifest仓库，即检查用来描述Manifest仓库MetaProject对象m的成员变量mExists值是否等于true。如果不等于的话，那么就说明本地还没有安装过Manifest仓库。这时候就需要调用该MetaProject对象m的成员函数_InitGitDir来在 .repo&#x2F;manifests 目录初始化一个Git仓库。</p>
<p>（2）调用用来描述Manifest仓库MetaProject对象m的成员函数Sync_NetworkHalf来从远程仓库中克隆一个新的Manifest仓库到本地来，或者更新本地的Manifest仓库。这个远程仓库的地址即为在执行”repo init”命令时，通过-u指定的url，即<a target="_blank" rel="noopener" href="https://android.googlesource.com/platform/manifest">https://android.googlesource.com/platform/manifest</a>。</p>
<p>（3）检查”repo init”命令后面是否通过-b指定要在Manifest仓库中checkout出来的分支。如果有的话，那么就调用用来描述Manifest仓库MetaProject对象m的成员函数MetaBranchSwitch做一些清理工作，以便接下来可以checkout到指定的分支。</p>
<p>（4）调用用来描述Manifest仓库MetaProject对象m的成员函数Sync_LocaHalf来执行checkout分支的操作。注意，要切换的分支在前面已经记录在MetaProject对象m的成员变量revisionExpr中。</p>
<p>（5）如果前面执行的是新安装Manifest仓库的操作，并且没有通过-b选项指定要checkout的分支，那么默认就checkout出一个default分支。</p>
<p>接下来，我们就主要分析MetaProject类的成员函数_InitGitDir、Sync_NetworkHalf和Sync_LocaHalf的实现。这几个函数实际上都是由MetaProject的父类Project来实现的，因此，下面我们就分析Project类的成员函数_InitGitDir、Sync_NetworkHalf和Sync_LocaHalf的实现。</p>
<p>Project类的成员函数_InitGitDir的成员函数的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">_InitGitDir</span>(<span class="params">self, mirror_git=<span class="literal">None</span></span>): </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(self.gitdir): </span><br><span class="line">   os.makedirs(self.gitdir) </span><br><span class="line">   self.bare_git.init() </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Project类的成员函数_InitGitDir首先是检查项目的Git目录是否已经存在。如果不存在，那么就会首先创建这个Git目录，然后再调用成员变量bare_git所描述的一个_GitGetByExec对象的成员函数init来在该目录下初始化一个Git仓库。</p>
<p>_GitGetByExec类的成员函数init是通过另外一个成员函数__getattr__来实现的，如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">_GitGetByExec</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">def</span> <span class="title function_">__getattr__</span>(<span class="params">self, name</span>): </span><br><span class="line">   <span class="string">&quot;&quot;&quot;Allow arbitrary git commands using pythonic syntax. </span></span><br><span class="line"><span class="string"> </span></span><br><span class="line"><span class="string">   This allows you to do things like: </span></span><br><span class="line"><span class="string">    git_obj.rev_parse(&#x27;HEAD&#x27;) </span></span><br><span class="line"><span class="string"> </span></span><br><span class="line"><span class="string">   Since we don&#x27;t have a &#x27;rev_parse&#x27; method defined, the __getattr__ will </span></span><br><span class="line"><span class="string">   run. We&#x27;ll replace the &#x27;_&#x27; with a &#x27;-&#x27; and try to run a git command. </span></span><br><span class="line"><span class="string">   Any other positional arguments will be passed to the git command, and the </span></span><br><span class="line"><span class="string">   following keyword arguments are supported: </span></span><br><span class="line"><span class="string">    config: An optional dict of git config options to be passed with &#x27;-c&#x27;. </span></span><br><span class="line"><span class="string"> </span></span><br><span class="line"><span class="string">   Args: </span></span><br><span class="line"><span class="string">    name: The name of the git command to call. Any &#x27;_&#x27; characters will </span></span><br><span class="line"><span class="string">      be replaced with &#x27;-&#x27;. </span></span><br><span class="line"><span class="string"> </span></span><br><span class="line"><span class="string">   Returns: </span></span><br><span class="line"><span class="string">    A callable object that will try to call git with the named command. </span></span><br><span class="line"><span class="string">   &quot;&quot;&quot;</span> </span><br><span class="line">   name = name.replace(<span class="string">&#x27;_&#x27;</span>, <span class="string">&#x27;-&#x27;</span>) </span><br><span class="line">   <span class="keyword">def</span> <span class="title function_">runner</span>(<span class="params">*args, **kwargs</span>): </span><br><span class="line">    cmdv = [] </span><br><span class="line">    config = kwargs.pop(<span class="string">&#x27;config&#x27;</span>, <span class="literal">None</span>) </span><br><span class="line">    <span class="comment">#...... </span></span><br><span class="line">    <span class="keyword">if</span> config <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>: </span><br><span class="line">     <span class="comment">#...... </span></span><br><span class="line">     <span class="keyword">for</span> k, v <span class="keyword">in</span> config.items(): </span><br><span class="line">      cmdv.append(<span class="string">&#x27;-c&#x27;</span>) </span><br><span class="line">      cmdv.append(<span class="string">&#x27;%s=%s&#x27;</span> % (k, v)) </span><br><span class="line">    cmdv.append(name) </span><br><span class="line">    cmdv.extend(args) </span><br><span class="line">    p = GitCommand(self._project, </span><br><span class="line">            cmdv, </span><br><span class="line">            bare = self._bare, </span><br><span class="line">            capture_stdout = <span class="literal">True</span>, </span><br><span class="line">            capture_stderr = <span class="literal">True</span>) </span><br><span class="line">    <span class="keyword">if</span> p.Wait() != <span class="number">0</span>: </span><br><span class="line">     <span class="comment">#...... </span></span><br><span class="line">    r = p.stdout </span><br><span class="line">    <span class="keyword">try</span>: </span><br><span class="line">     r = r.decode(<span class="string">&#x27;utf-8&#x27;</span>) </span><br><span class="line">    <span class="keyword">except</span> AttributeError: </span><br><span class="line">     <span class="keyword">pass</span> </span><br><span class="line">    <span class="keyword">if</span> r.endswith(<span class="string">&#x27;\n&#x27;</span>) <span class="keyword">and</span> r.index(<span class="string">&#x27;\n&#x27;</span>) == <span class="built_in">len</span>(r) - <span class="number">1</span>: </span><br><span class="line">     <span class="keyword">return</span> r[:-<span class="number">1</span>] </span><br><span class="line">    <span class="keyword">return</span> r </span><br><span class="line">   <span class="keyword">return</span> runner </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>从注释可以知道，_GitGetByExec类的成员函数__getattr__使用了一个trick，将_GitGetByExec类没有实现的成员函数间接地以属性的形式来获得，并且将该没有实现的成员函数的名称作为git的一个参数来执行。也就是说，当执行_GitGetByExec.init()的时候，实际上是透过成员函数__getattr__执行了一个”git init”命令。这个命令就正好是用来初始化一个Git仓库。</p>
<p>我们再来看Project类的成员函数Sync_NetworkHalf的实现：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="comment">#......  </span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Sync_NetworkHalf</span>(<span class="params">self, </span></span><br><span class="line"><span class="params">   quiet=<span class="literal">False</span>, </span></span><br><span class="line"><span class="params">   is_new=<span class="literal">None</span>, </span></span><br><span class="line"><span class="params">   current_branch_only=<span class="literal">False</span>, </span></span><br><span class="line"><span class="params">   clone_bundle=<span class="literal">True</span>, </span></span><br><span class="line"><span class="params">   no_tags=<span class="literal">False</span></span>): </span><br><span class="line">  <span class="string">&quot;&quot;&quot;Perform only the network IO portion of the sync process. </span></span><br><span class="line"><span class="string">    Local working directory/branch state is not affected. </span></span><br><span class="line"><span class="string">  &quot;&quot;&quot;</span> </span><br><span class="line">  <span class="keyword">if</span> is_new <span class="keyword">is</span> <span class="literal">None</span>: </span><br><span class="line">   is_new = <span class="keyword">not</span> self.Exists </span><br><span class="line">  <span class="keyword">if</span> is_new: </span><br><span class="line">   self._InitGitDir()  </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir, </span><br><span class="line">               current_branch_only=current_branch_only, </span><br><span class="line">               no_tags=no_tags): </span><br><span class="line">   <span class="keyword">return</span> <span class="literal">False</span> </span><br><span class="line">    <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Project类的成员函数Sync_NetworkHalf主要执行以下的操作：</p>
<p>（1）检查本地是否已经存在对应的Git仓库。如果不存在，那么就先调用另外一个成员函数_InitGitDir来初始化该Git仓库。</p>
<p>（2）调用另外一个成员函数_RemoteFetch来从远程仓库更新本地仓库。</p>
<p>Project类的成员函数_RemoteFetch的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"><span class="comment">#......  </span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">_RemoteFetch</span>(<span class="params">self, name=<span class="literal">None</span>, </span></span><br><span class="line"><span class="params">          current_branch_only=<span class="literal">False</span>, </span></span><br><span class="line"><span class="params">          initial=<span class="literal">False</span>, </span></span><br><span class="line"><span class="params">          quiet=<span class="literal">False</span>, </span></span><br><span class="line"><span class="params">          alt_dir=<span class="literal">None</span>, </span></span><br><span class="line"><span class="params">          no_tags=<span class="literal">False</span></span>): </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  cmd = [<span class="string">&#x27;fetch&#x27;</span>] </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  ok = <span class="literal">False</span> </span><br><span class="line">  <span class="keyword">for</span> _i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">2</span>): </span><br><span class="line">   ret = GitCommand(self, cmd, bare=<span class="literal">True</span>, ssh_proxy=ssh_proxy).Wait() </span><br><span class="line">   <span class="keyword">if</span> ret == <span class="number">0</span>: </span><br><span class="line">    ok = <span class="literal">True</span> </span><br><span class="line">    <span class="keyword">break</span> </span><br><span class="line">   <span class="keyword">elif</span> current_branch_only <span class="keyword">and</span> is_sha1 <span class="keyword">and</span> ret == <span class="number">128</span>: </span><br><span class="line">    <span class="comment"># Exit code 128 means &quot;couldn&#x27;t find the ref you asked for&quot;; if we&#x27;re in sha1 </span></span><br><span class="line">    <span class="comment"># mode, we just tried sync&#x27;ing from the upstream field; it doesn&#x27;t exist, thus </span></span><br><span class="line">    <span class="comment"># abort the optimization attempt and do a full sync. </span></span><br><span class="line">    <span class="keyword">break</span> </span><br><span class="line">   time.sleep(random.randint(<span class="number">30</span>, <span class="number">45</span>))    </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Project类的成员函数_RemoteFetch的核心操作就是调用“git fetch”命令来从远程仓库更新本地仓库。</p>
<p>接下来我们再看MetaProject类的成员函数Sync_LocaHalf的实现：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="comment">#......  </span></span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Sync_LocalHalf</span>(<span class="params">self, syncbuf</span>): </span><br><span class="line">  <span class="string">&quot;&quot;&quot;Perform only the local IO portion of the sync process. </span></span><br><span class="line"><span class="string">    Network access is not required. </span></span><br><span class="line"><span class="string">  &quot;&quot;&quot;</span> </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line">  revid = self.GetRevisionId(all_refs) </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   self._InitWorkTree() </span><br><span class="line">  head = self.work_git.GetHead() </span><br><span class="line">  <span class="keyword">if</span> head.startswith(R_HEADS): </span><br><span class="line">   branch = head[<span class="built_in">len</span>(R_HEADS):] </span><br><span class="line">   <span class="keyword">try</span>: </span><br><span class="line">    head = all_refs[head] </span><br><span class="line">   <span class="keyword">except</span> KeyError: </span><br><span class="line">    head = <span class="literal">None</span> </span><br><span class="line">  <span class="keyword">else</span>: </span><br><span class="line">   branch = <span class="literal">None</span> </span><br><span class="line"> </span><br><span class="line">  <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">if</span> head == revid: </span><br><span class="line">   <span class="comment"># No changes; don&#x27;t do anything further. </span></span><br><span class="line">   <span class="comment"># </span></span><br><span class="line">   <span class="keyword">return</span> </span><br><span class="line"> </span><br><span class="line">  branch = self.GetBranch(branch) </span><br><span class="line">   <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> branch.LocalMerge: </span><br><span class="line">   <span class="comment"># The current branch has no tracking configuration. </span></span><br><span class="line">   <span class="comment"># Jump off it to a detached HEAD. </span></span><br><span class="line">   <span class="comment"># </span></span><br><span class="line">   syncbuf.info(self, </span><br><span class="line">          <span class="string">&quot;leaving %s; does not track upstream&quot;</span>, </span><br><span class="line">          branch.name) </span><br><span class="line">   <span class="keyword">try</span>: </span><br><span class="line">    self._Checkout(revid, quiet=<span class="literal">True</span>) </span><br><span class="line">   <span class="keyword">except</span> GitError <span class="keyword">as</span> e: </span><br><span class="line">    syncbuf.fail(self, e) </span><br><span class="line">    <span class="keyword">return</span> </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   <span class="keyword">return</span>  </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>这里我们只分析一种比较简单的情况，就是当前要checkout的分支是一个干净的分支，它没有做过修改，也没有设置跟踪远程分支。这时候Project类的成员函数_RemoteFetch的主要执行以下操作：</p>
<p>（1）调用另外一个成员函数GetRevisionId获得即将要checkout的分支，保存在变量revid中。</p>
<p>（2）调用成员变量work_git所描述的一个_GitGetByExec对象的成员函数GetHead获得项目当前checkout的分支，只存在变量head中。</p>
<p>（3）如果即将要checkout的分支revid就是当前已经checkout分支，那么就什么也不用做。否则继续往下执行。</p>
<p>（4）调用另外一个成员函数GetBranch获得用来描述当前分支的一个Branch对象。</p>
<p>（5）如果上述Branch对象的属性LocalMerge的值等于None，也就是属于我们讨论的情况，那么就调用另外一个成员函数_Checkout真正执行checkout分支revid的操作。</p>
<p>如果要checkout的分支revid不是一个干净的分支，也就是它正在跟踪远程分支，并且在本地做过提交，这些提交又没有上传到远程分支去，那么就需要执行一些merge或者rebase的操作。不过无论如何，这些操作都是通过标准的Git命令来完成的。</p>
<p>我们接着再看Project类的成员函数_Checkout的实现：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">_Checkout</span>(<span class="params">self, rev, quiet=<span class="literal">False</span></span>): </span><br><span class="line">  cmd = [<span class="string">&#x27;checkout&#x27;</span>] </span><br><span class="line">  <span class="keyword">if</span> quiet: </span><br><span class="line">   cmd.append(<span class="string">&#x27;-q&#x27;</span>) </span><br><span class="line">  cmd.append(rev) </span><br><span class="line">  cmd.append(<span class="string">&#x27;--&#x27;</span>) </span><br><span class="line">  <span class="keyword">if</span> GitCommand(self, cmd).Wait() != <span class="number">0</span>: </span><br><span class="line">   <span class="keyword">if</span> self._allrefs: </span><br><span class="line">    <span class="keyword">raise</span> GitError(<span class="string">&#x27;%s checkout %s &#x27;</span> % (self.name, rev)) </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Project类的成员函数_Checkout的实现很简单，它通过GitCommand类构造了一个“git checkout”命令，将参数rev描述的分支checkout出来。</p>
<p>至此，我们就将Manifest仓库从远程地址<a target="_blank" rel="noopener" href="https://android.googlesource.com/platform/manifest">https://android.googlesource.com/platform/manifest</a>克隆到本地来了，并且checkout出了指定的分支。回到Init类的成员函数Execute中，它接下来还要调用另外一个成员函数_LinkManifest来执行一个符号链接的操作。</p>
<p>Init类的成员函数_LinkManifest的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Init</span>(InteractiveCommand, MirrorSafeCommand): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">_LinkManifest</span>(<span class="params">self, name</span>): </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> name: </span><br><span class="line">   <span class="built_in">print</span>(<span class="string">&#x27;fatal: manifest name (-m) is required.&#x27;</span>, file=sys.stderr) </span><br><span class="line">   sys.exit(<span class="number">1</span>) </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   self.manifest.Link(name) </span><br><span class="line">  <span class="keyword">except</span> ManifestParseError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="built_in">print</span>(<span class="string">&quot;fatal: manifest &#x27;%s&#x27; not available&quot;</span> % name, file=sys.stderr) </span><br><span class="line">   <span class="built_in">print</span>(<span class="string">&#x27;fatal: %s&#x27;</span> % <span class="built_in">str</span>(e), file=sys.stderr) </span><br><span class="line">   sys.exit(<span class="number">1</span>) </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>参数name的值一般就等于“default.xml”，表示Manifest仓库中的default.xml文件，Init类的成员函数_LinkManifest通过调用成员变量manifest所描述的一个XmlManifest对象的成员函数Link来执行符号链接的操作，它定义在文件.repo&#x2F;repo&#x2F;xml_manifest.py文件，它的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">XmlManifest</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> <span class="string">&quot;&quot;&quot;manages the repo configuration file&quot;&quot;&quot;</span> </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Link</span>(<span class="params">self, name</span>): </span><br><span class="line">  <span class="string">&quot;&quot;&quot;Update the repo metadata to use a different manifest. </span></span><br><span class="line"><span class="string">  &quot;&quot;&quot;</span> </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   <span class="keyword">if</span> os.path.lexists(self.manifestFile): </span><br><span class="line">    os.remove(self.manifestFile) </span><br><span class="line">   os.symlink(<span class="string">&#x27;manifests/%s&#x27;</span> % name, self.manifestFile) </span><br><span class="line">  <span class="keyword">except</span> OSError <span class="keyword">as</span> e: </span><br><span class="line">   <span class="keyword">raise</span> ManifestParseError(<span class="string">&#x27;cannot link manifest %s: %s&#x27;</span> % (name, <span class="built_in">str</span>(e))) </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>XmlManifest类的成员变量manifestFile的值等于<code>$(AOSP)/.repo/manifest.xml</code>，通过调用os.symlink就将它符号链接至<code>$(AOSP)/.repo/manifests/&lt;name&gt;</code> 文件去。这样无论Manifest仓库中用来描述AOSP子项目的xml文件是什么名称，都可以统一通过<code>$(AOSP)/.repo/manifest.xml</code>文件来访问。</p>
<p>前面提到，Manifest仓库中用来描述AOSP子项目的xml文件名称默认就为default.xml，它的内容如下所示：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span> </span><br><span class="line"><span class="tag">&lt;<span class="name">manifest</span>&gt;</span>  </span><br><span class="line"> <span class="tag">&lt;<span class="name">remote</span> <span class="attr">name</span>=<span class="string">&quot;aosp&quot;</span> </span></span><br><span class="line"><span class="tag">      <span class="attr">fetch</span>=<span class="string">&quot;..&quot;</span> </span></span><br><span class="line"><span class="tag">      <span class="attr">review</span>=<span class="string">&quot;https://android-review.googlesource.com/&quot;</span> /&gt;</span> </span><br><span class="line"> <span class="tag">&lt;<span class="name">default</span> <span class="attr">revision</span>=<span class="string">&quot;refs/tags/android-4.2_r1&quot;</span> </span></span><br><span class="line"><span class="tag">      <span class="attr">remote</span>=<span class="string">&quot;aosp&quot;</span> </span></span><br><span class="line"><span class="tag">      <span class="attr">sync-j</span>=<span class="string">&quot;4&quot;</span> /&gt;</span>  </span><br><span class="line"> <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build&quot;</span> &gt;</span> </span><br><span class="line">  <span class="tag">&lt;<span class="name">copyfile</span> <span class="attr">src</span>=<span class="string">&quot;core/root.mk&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;Makefile&quot;</span> /&gt;</span> </span><br><span class="line"> <span class="tag">&lt;/<span class="name">project</span>&gt;</span> </span><br><span class="line"> <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;abi/cpp&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/abi/cpp&quot;</span> /&gt;</span> </span><br><span class="line"> <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;bionic&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/bionic&quot;</span> /&gt;</span> </span><br><span class="line"> <span class="comment">&lt;!-- ...... --&gt;</span> </span><br><span class="line"><span class="tag">&lt;/<span class="name">manifest</span>&gt;</span> </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>关于该xml文件的详细描述可以参考.repo&#x2F;repo&#x2F;docs&#x2F;manifest-format.txt文件。一般来说，该xml包含有四种类型的标签：</p>
<p>remote：用来指定远程仓库信息。属性name描述的是一个远程仓库的名称，属性fetch用作项目名称的前缘，在构造项目仓库远程地址时使用到，属性review描述的是用作code review的server地址。</p>
<p>default：当project标签没有指定default标签的属性时，默认就使用在default标签列出的属性。属性revision描述的是项目默认检出的分支，属性remote描述的是默认使用的远程仓库名称，必须要对应的remote标签的name属性值，属性sync-j描述的是从远程仓库更新项目时使用的并行任务数。</p>
<p>project：每一个AOSP子项目在这里都对应有一个projec标签，用来描述项目的元信息。属性path描述的是项目相对于远程仓库URL的路径，属性name描述的是项目的名称，也是相对于 AOSP根目录的目录名称。例如，如果远程仓库URL为<a target="_blank" rel="noopener" href="https://android.googlesource.com/platform">https://android.googlesource.com/platform</a>，那么AOSP子项目bionic对应的远程仓库URL就为<a target="_blank" rel="noopener" href="https://android.googlesource.com/platform/bionic">https://android.googlesource.com/platform/bionic</a>，并且它的工作目录位于<code>$(AOSP)/bionic</code>。</p>
<p>copyfile：作为project的子标签，表示要将从远程仓库更新回来的文件拷贝到指定的另外一个文件去。</p>
<p>至些，我们就分析完成Manifest仓库的克隆过程了。在此基础上，我们再分析AOSP子项目仓库的克隆过程或者针对AOSP子项目的各种Repo命令就容易多了。</p>
<h3 id="2-4-AOSP子项目仓库"><a href="#2-4-AOSP子项目仓库" class="headerlink" title="2.4 AOSP子项目仓库"></a><font size=3>2.4 AOSP子项目仓库</font></h3><blockquote>
<p>【注意】这一部分我就不是按自己下载的最新的repo仓库源码分析的了，直接复制的原文：<a target="_blank" rel="noopener" href="https://www.jb51.net/article/131935.htm">Android源代码仓库及其管理工具Repo分析详解_Android_脚本之家 (jb51.net)</a>，主要是这里没看懂。哈哈。</p>
</blockquote>
<p>执行完成repo init命令之后，我们就可以继续执行repo sync命令来克隆或者同步AOSP子项目了：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo sync</span><br></pre></td></tr></table></figure>

<p>与repo init命令类似，repo sync命令的执行过程如下所示：</p>
<p>（1）Repo脚本找到Repo仓库里面的main.py文件，并且执行它的入口函数_Main；</p>
<p>（2）Repo仓库里面的main.py文件的入口函数_Main调用_Repo类的成员函数_Run对Repo脚本传递进来的参数进行解析；</p>
<p>（3）_Repo类的成员函数_Run解析参数发现要执行的命令是sync，于是就在subcmds目录中找到一个名称为sync.py的文件，并且调用定义在它里面的一个名称为Sync的类的成员函数Execute；</p>
<p>（4）Sync类的成员函数Execute解析Manifest仓库的default.xml文件，并且克隆或者同步出在default.xml文件里面列出的每一个AOSP子项目。</p>
<p>在第（3）步中，Repo仓库的每一个Python文件是如何与一个Repo命令关联起来的呢？原来在Repo仓库的subcmds目录中，有一个__init__.py文件，每当subcmds被import时，定义在它里面的命令就会被执行，如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">all_commands = &#123;&#125;  </span><br><span class="line">my_dir = os.path.dirname(__file__) </span><br><span class="line"><span class="keyword">for</span> py <span class="keyword">in</span> os.listdir(my_dir): </span><br><span class="line"> <span class="keyword">if</span> py == <span class="string">&#x27;__init__.py&#x27;</span>: </span><br><span class="line">  <span class="keyword">continue</span>  </span><br><span class="line"> <span class="keyword">if</span> py.endswith(<span class="string">&#x27;.py&#x27;</span>): </span><br><span class="line">  name = py[:-<span class="number">3</span>] </span><br><span class="line">  clsn = name.capitalize() </span><br><span class="line">  <span class="keyword">while</span> clsn.find(<span class="string">&#x27;_&#x27;</span>) &gt; <span class="number">0</span>: </span><br><span class="line">   h = clsn.index(<span class="string">&#x27;_&#x27;</span>) </span><br><span class="line">   clsn = clsn[<span class="number">0</span>:h] + clsn[h + <span class="number">1</span>:].capitalize() </span><br><span class="line">   mod = <span class="built_in">__import__</span>(__name__, </span><br><span class="line">           <span class="built_in">globals</span>(), </span><br><span class="line">           <span class="built_in">locals</span>(), </span><br><span class="line">           [<span class="string">&#x27;%s&#x27;</span> % name]) </span><br><span class="line">  mod = <span class="built_in">getattr</span>(mod, name) </span><br><span class="line">  <span class="keyword">try</span>: </span><br><span class="line">   cmd = <span class="built_in">getattr</span>(mod, clsn)() </span><br><span class="line">  <span class="keyword">except</span> AttributeError: </span><br><span class="line">   <span class="keyword">raise</span> SyntaxError(<span class="string">&#x27;%s/%s does not define class %s&#x27;</span> % ( </span><br><span class="line">             __name__, py, clsn)) </span><br><span class="line"> </span><br><span class="line">  name = name.replace(<span class="string">&#x27;_&#x27;</span>, <span class="string">&#x27;-&#x27;</span>) </span><br><span class="line">  cmd.NAME = name </span><br><span class="line">  all_commands[name] = cmd </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>__init__.py会列出subcmds目录中的所有Python文件（除了__init__.py），并且里面找到对应的类，然后再创建这个类的一个对象，并且以文件名为关键字将该对象保存在全局变量all_commands中。例如，对于sync.py文件，它的文件名称去掉后缀名后为sync，再将sync的首字母大写，得到Sync。也就是说，sync.py需要定义一个Sync类，并且这个类需要直接或者间接地从Command类继承下来。Command 类有一个成员函数Execute，它的各个子类需要对它进行重写，以实现各自的功能。</p>
<p>_Repo类的成员函数_Run就是通过subcmds模块里面的全局变量all_commands，并且根据Repo脚本传进行来的第一个不带横线“-”的参数来找到对应的Command对象，然后调用它的成员函数Execute的。</p>
<p>Sync类的成员函数Execute的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Sync</span>(Command, MirrorSafeCommand): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Execute</span>(<span class="params">self, opt, args</span>): </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  mp = self.manifest.manifestProject </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> opt.local_only: </span><br><span class="line">   mp.Sync_NetworkHalf(quiet=opt.quiet, </span><br><span class="line">             current_branch_only=opt.current_branch_only, </span><br><span class="line">             no_tags=opt.no_tags) </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> mp.HasChanges: </span><br><span class="line">   ...... </span><br><span class="line">   mp.Sync_LocalHalf(syncbuf) </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  all_projects = self.GetProjects(args, </span><br><span class="line">                  missing_ok=<span class="literal">True</span>, </span><br><span class="line">                  submodules_ok=opt.fetch_submodules) </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> opt.local_only: </span><br><span class="line">   to_fetch = [] </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   to_fetch.extend(all_projects) </span><br><span class="line">   to_fetch.sort(key=self._fetch_times.Get, reverse=<span class="literal">True</span>) </span><br><span class="line"> </span><br><span class="line">   fetched = self._Fetch(to_fetch, opt) </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">   <span class="keyword">if</span> opt.network_only: </span><br><span class="line">    <span class="comment"># bail out now; the rest touches the working tree </span></span><br><span class="line">    <span class="keyword">return</span> </span><br><span class="line"> </span><br><span class="line">   <span class="comment"># Iteratively fetch missing and/or nested unregistered submodules </span></span><br><span class="line">   <span class="keyword">while</span> <span class="literal">True</span>: </span><br><span class="line">    <span class="comment">#...... </span></span><br><span class="line">    all_projects = self.GetProjects(args, </span><br><span class="line">                    missing_ok=<span class="literal">True</span>, </span><br><span class="line">                    submodules_ok=opt.fetch_submodules) </span><br><span class="line">    missing = [] </span><br><span class="line">    <span class="keyword">for</span> project <span class="keyword">in</span> all_projects: </span><br><span class="line">     <span class="keyword">if</span> project.gitdir <span class="keyword">not</span> <span class="keyword">in</span> fetched: </span><br><span class="line">      missing.append(project) </span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> missing: </span><br><span class="line">     <span class="keyword">break</span> </span><br><span class="line">    <span class="comment">#...... </span></span><br><span class="line">    fetched.update(self._Fetch(missing, opt)) </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> self.UpdateProjectList(): </span><br><span class="line">   sys.exit(<span class="number">1</span>) </span><br><span class="line">   <span class="comment">#......  </span></span><br><span class="line">  <span class="keyword">for</span> project <span class="keyword">in</span> all_projects: </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   <span class="keyword">if</span> project.worktree: </span><br><span class="line">    project.Sync_LocalHalf(syncbuf)  </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Sync类的成员函数Execute的核以执行流程如下所示：</p>
<p>（1）获得用来描述Manifest仓库的MetaProject对象mp。</p>
<p>（2）如果在执行repo sync命令时，没有指定–local-only选项，那么就调用MetaProject对象mp的成员函数Sync_NetworkHalf从远程仓库下载更新本地Manifest仓库。</p>
<p>（3）如果Mainifest仓库发生过更新，那么就调用MetaProject对象mp的成员函数Sync_LocalHalf来合并这些更新到本地的当前分支来。</p>
<p>（4）调用Sync的父类Command的成员函数GetProjects获得由Manifest仓库的default.xml文件定义的所有AOSP子项目信息，或者由参数args所指定的AOSP子项目的信息。这些AOSP子项目信息都是通过Project对象来描述，并且保存在变量all_projects中。</p>
<p>（5）如果在执行repo sync命令时，没有指定–local-only选项，那么就对保存在变量all_projects中的AOSP子项目进行网络更新，也就是从远程仓库中下载更新到本地仓库来，这是通过调用Sync类的成员函数_Fetch来完成的。Sync类的成员函数_Fetch实际上又是通过调用Project类的成员函数Sync_NetworkHalf来将远程仓库的更新下载到本地仓库来的。</p>
<p>（6）由于AOSP子项目可能会包含有子模块，因此当对它们进行了远程更新之后，需要检查它们是否包含有子模块。如果包含有子模块，并且执行repo sync脚本时指定有–fetch-submodules选项，那么就需要对AOSP子项目的子模块进行远程更新。调用Sync的父类Command的成员函数GetProjects的时候，如果将参数submodules_ok的值设置为true，那么得到的AOSP子项目列表就包含有子模块。将这个AOSP子项目列表与之前获得的AOSP子项目列表fetched进行一个比较，就可以知道有哪些子模块是需要更新的。需要更新的子模块都保存在变量missing中。由于子模块也是用Project类来描述的，因此，我们可以像远程更新AOSP子项目一样，调用Sync类的成员函数_Fetch来更新它们的子模块。</p>
<p>（7）调用Sync类的成员函数UpdateProjectList更新<code>$(AOSP)/.repo</code>目录下的project.list文件。<code>$(AOSP)/.repo/project.list</code>记录的是上一次远程同步后所有的AOSP子项目名称。以后每一次远程同步之后，Sync类的成员函数UpdateProjectList就会通过该文件来检查是否存在某些AOSP子项目被删掉了。如果存在这样的AOSP子项目，并且这些AOSP子项目没有发生修改，那么就会将它们的工作目录删掉。</p>
<p>（8）到目前为止，Sync类的成员函数对AOSP子项目所做的操作仅仅是下载远程仓库的更新到本地来，但是还没有将这些更新合并到本地的当前分支来，因此，这时候就需要调用Project类的成员函数Sync_LocalHalf来执行合并更新的操作。</p>
<p>从上面的步骤可以看出，init sync命令的核心操作就是收集每一个需要同步的AOSP子项目所对应的Project对象，然后再调用这些Project对象的成员函数Sync_NetwokHalft和Sync_LocalHalf进行同步。关于Project类的成员函数Sync_NetwokHalft和Sync_LocalHalf，我们在前面分析Manifest仓库的克隆过程时，已经分析过了，它们无非就是通过git fetch、git rebase或者git merge等基本Git命令来完成自己的功能。</p>
<h3 id="2-5-在AOSP上创建Topic"><a href="#2-5-在AOSP上创建Topic" class="headerlink" title="2.5 在AOSP上创建Topic"></a><font size=3>2.5 在AOSP上创建Topic</font></h3><p>以上我们分析的就是AOSP子项目仓库的克隆或者同步过程，为了更进一步加深对Repo仓库的理解，接下来我们再学习另外一个用来在AOSP上创建Topic的命令repo start。</p>
<p>在Git的世界里，分支（branch）是一个很核心的概念。Git鼓励我们在修复Bug或者开发新的Feature时，都创建一个新的分支。创建Git分支的代价是很小的，而且速度很快，因此，不用担心创建Git分支是一件不讨好的事情，而应该尽可能多地使用分支。</p>
<p>同样的，我们下载好AOSP代码之后，如果需要在上面进行修改，或者增加新的功能，那么就要在新的分支上面进行。Repo仓库提供了一个repo start命令，用来在AOSP上创建分支，也称为Topic。这个命令的用法如下所示：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">repo start BRANCH_NAME [PROJECT_LIST] </span><br></pre></td></tr></table></figure>

<p>参数BRANCH_NAME指定新的分支名称，后面的PROJECT_LIST是可选的。如果指定了PROJECT_LIST，就表示只对特定的AOSP子项目创建分支，否则的话，就对所有的AOSP子项目创建分支。</p>
<p>根据前面我们对repo sync命令的分析可以知道，当我们执行repo start命令的时候，最终定义在Repo仓库的subcmds&#x2F;start.py文件里面的Start类的成员函数Execute会被调用，它的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Start</span>(<span class="title class_ inherited__">Command</span>): </span><br><span class="line"> <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">Execute</span>(<span class="params">self, opt, args</span>): </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"> </span><br><span class="line">  nb = args[<span class="number">0</span>] </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> git.check_ref_format(<span class="string">&#x27;heads/%s&#x27;</span> % nb): </span><br><span class="line">   <span class="built_in">print</span>(<span class="string">&quot;error: &#x27;%s&#x27; is not a valid name&quot;</span> % nb, file=sys.stderr) </span><br><span class="line">   sys.exit(<span class="number">1</span>) </span><br><span class="line"> </span><br><span class="line">  err = [] </span><br><span class="line">  projects = [] </span><br><span class="line">  <span class="keyword">if</span> <span class="keyword">not</span> opt.<span class="built_in">all</span>: </span><br><span class="line">   projects = args[<span class="number">1</span>:] </span><br><span class="line">   <span class="keyword">if</span> <span class="built_in">len</span>(projects) &lt; <span class="number">1</span>: </span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&quot;error: at least one project must be specified&quot;</span>, file=sys.stderr) </span><br><span class="line">    sys.exit(<span class="number">1</span>) </span><br><span class="line"> </span><br><span class="line">  all_projects = self.GetProjects(projects) </span><br><span class="line"> </span><br><span class="line">  pm = Progress(<span class="string">&#x27;Starting %s&#x27;</span> % nb, <span class="built_in">len</span>(all_projects)) </span><br><span class="line">  <span class="keyword">for</span> project <span class="keyword">in</span> all_projects: </span><br><span class="line">   pm.update() </span><br><span class="line">   <span class="comment">#...... </span></span><br><span class="line">   <span class="keyword">if</span> <span class="keyword">not</span> project.StartBranch(nb): </span><br><span class="line">    err.append(project) </span><br><span class="line">  pm.end() </span><br><span class="line"> </span><br><span class="line">  <span class="comment">#...... </span></span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>参数args[0]保存的是要创建的分支的名称，参数args[1:]保存的是要创建分支的AOSP子项目名称列表，Start类的成员函数Execute分别将它们保存变量nb和projects中。</p>
<p>Start类的成员函数Execute接下来调用父类Command的成员函数GetProjects，并且以变量projects为参数，就可以获得所有需要创建新分支nb的AOSP子项目列表all_projects。在all_projects中，每一个AOSP子项目都用一个Project对象来描述。</p>
<p>最后，Start类的成员函数Execute就遍历all_projects里面的每一个Project对象，并且调用它们的成员函数StartBranch来执行创建新分支的操作。</p>
<p>Project类的成员函数StartBranch的实现如下所示：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Project</span>(<span class="title class_ inherited__">object</span>): </span><br><span class="line"> ...... </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">def</span> <span class="title function_">StartBranch</span>(<span class="params">self, name</span>): </span><br><span class="line">  <span class="string">&quot;&quot;&quot;Create a new branch off the manifest&#x27;s revision. </span></span><br><span class="line"><span class="string">  &quot;&quot;&quot;</span> </span><br><span class="line">  head = self.work_git.GetHead() </span><br><span class="line">  <span class="keyword">if</span> head == (R_HEADS + name): </span><br><span class="line">   <span class="keyword">return</span> <span class="literal">True</span> </span><br><span class="line"> </span><br><span class="line">  all_refs = self.bare_ref.<span class="built_in">all</span> </span><br><span class="line">  <span class="keyword">if</span> (R_HEADS + name) <span class="keyword">in</span> all_refs: </span><br><span class="line">   <span class="keyword">return</span> GitCommand(self, </span><br><span class="line">            [<span class="string">&#x27;checkout&#x27;</span>, name, <span class="string">&#x27;--&#x27;</span>], </span><br><span class="line">            capture_stdout = <span class="literal">True</span>, </span><br><span class="line">            capture_stderr = <span class="literal">True</span>).Wait() == <span class="number">0</span> </span><br><span class="line"> </span><br><span class="line">  branch = self.GetBranch(name) </span><br><span class="line">  branch.remote = self.GetRemote(self.remote.name) </span><br><span class="line">  branch.merge = self.revisionExpr </span><br><span class="line">  revid = self.GetRevisionId(all_refs) </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> head.startswith(R_HEADS): </span><br><span class="line">   <span class="keyword">try</span>: </span><br><span class="line">    head = all_refs[head] </span><br><span class="line">   <span class="keyword">except</span> KeyError: </span><br><span class="line">    head = <span class="literal">None</span> </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> revid <span class="keyword">and</span> head <span class="keyword">and</span> revid == head: </span><br><span class="line">   ref = os.path.join(self.gitdir, R_HEADS + name) </span><br><span class="line">   <span class="keyword">try</span>: </span><br><span class="line">    os.makedirs(os.path.dirname(ref)) </span><br><span class="line">   <span class="keyword">except</span> OSError: </span><br><span class="line">    <span class="keyword">pass</span> </span><br><span class="line">   _lwrite(ref, <span class="string">&#x27;%s\n&#x27;</span> % revid) </span><br><span class="line">   _lwrite(os.path.join(self.worktree, <span class="string">&#x27;.git&#x27;</span>, HEAD), </span><br><span class="line">       <span class="string">&#x27;ref: %s%s\n&#x27;</span> % (R_HEADS, name)) </span><br><span class="line">   branch.Save() </span><br><span class="line">   <span class="keyword">return</span> <span class="literal">True</span> </span><br><span class="line"> </span><br><span class="line">  <span class="keyword">if</span> GitCommand(self, </span><br><span class="line">         [<span class="string">&#x27;checkout&#x27;</span>, <span class="string">&#x27;-b&#x27;</span>, branch.name, revid], </span><br><span class="line">         capture_stdout = <span class="literal">True</span>, </span><br><span class="line">         capture_stderr = <span class="literal">True</span>).Wait() == <span class="number">0</span>: </span><br><span class="line">   branch.Save() </span><br><span class="line">   <span class="keyword">return</span> <span class="literal">True</span> </span><br><span class="line">  <span class="keyword">return</span> <span class="literal">False</span> </span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p>Project类的成员函数StartBranch的执行过程如下所示：</p>
<p>（1）获得项目的当前分支head，这是通过调用Project类的成员函数GetHead来实现的。</p>
<p>（2）项目当前的所有分支保存在Project类的成员变量bare_ref所描述的一个GitRefs对象的成员变量all中。如果要创建的分支name已经项目的一个分支，那么就直接通过GitCommand类调用git checkout命令来将该分支检出即可，而不用创建新的分支。否则继续往下执行。</p>
<p>（3）创建一个Branch对象来描述即将要创建的分支。Branch类的成员变量remote描述的分支所要追踪的远程仓库，另外一个成员变量merge描述的是分支要追踪的远程仓库的分支。这个要追踪的远程仓库分支由Manifest仓库的default.xml文件描述，并且保存在Project类的成员变量revisionExpr中。</p>
<p>（4）调用Project类的成员函数GetRevisionId获得项目要追踪的远程仓库分支的sha1值，并且保存在变量revid中。</p>
<p>（5）由于新创建的分支name需要追踪的远程仓库分支为revid，因此如果项目的当前分支head刚好就是项目要追踪的远程仓库分支revid，那么创建新分支name就变得很简单，只要在项目的Git目录（位于.repo&#x2F;projects目录下）下的refs&#x2F;heads子目录以name名称创建一个文件，并且往这个文件写入写入revid的值，以表明新分支name是在要追踪的远程分支revid的基础上创建的。这样的一个简单的Git分支就创建完成了。不过我们还要修改项目工作目录下的.git&#x2F;HEAD文件，将它的内容写为刚才创建的文件的路径名称，这样才能将项目的当前分支切换为刚才新创建的分支。从这个过程就可以看出，创建的一个Git分支，不过就是创建一个包含一个sha1值的文件，因此代价是非常小的。如果项目的当前分支head刚好不是项目要追踪的远程仓库分支revid，那么就继续往下执行。</p>
<p>（6）执行到这里的时候，就表明我们要创建的分支不存在，并且我们需要在一个不是当前分支的分支的基础上创建该新分支，这时候就需要通过调用带-b选项的git checkout命令来完成创建新分支的操作了。选项-b后面的参数就表明要在哪一个分支的基础上创建分支。新的分支创建出来之后，还需要将它的文件拷贝到项目的工作目录去。</p>
<p>至此，我们就分析完在AOSP上创建新分支的过程了，也就是repo start命令的执行过程。更多的repo命令，例如repo uplad、repo diff和repo status等，可以以参考官方文档<a target="_blank" rel="noopener" href="http://source.android.com/source/using-repo.html">http://source.android.com/source/using-repo.html</a>，它们的执行过程和我们前面分析repo sync、repo start都是类似，不同的是它们执行其它的Git命令。</p>
<h1 id="二、-repo相关目录"><a href="#二、-repo相关目录" class="headerlink" title="二、.repo相关目录"></a><font size=3>二、<code>.repo</code>相关目录</font></h1><p>前面我们知道，在初始化一个项目时，就会从远程把manifests和repo这两个git库拷贝到本地，上面下载完毕后，会发现出现了这两个仓库：</p>
<img data-src="https://fanhua-picture.oss-cn-hangzhou.aliyuncs.com/02%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/03%E7%89%88%E6%9C%AC%E6%8E%A7%E5%88%B6/LV03-repo-02-repo%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/img/image-20240926233044031.png" alt="image-20240926233044031" style="zoom:50%;" />

<p>可以看到这两个仓库都是git版本库。</p>
<h2 id="1-项目清单库-repo-manifests"><a href="#1-项目清单库-repo-manifests" class="headerlink" title="1. 项目清单库(.repo&#x2F;manifests)"></a><font size=3>1. 项目清单库(.repo&#x2F;manifests)</font></h2><p>AOSP项目清单git库下，只有一个文件default.xml，是一个标准的XML，描述了当前repo管理的所有信息。</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">manifest</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">remote</span>  <span class="attr">name</span>=<span class="string">&quot;aosp&quot;</span></span></span><br><span class="line"><span class="tag">           <span class="attr">fetch</span>=<span class="string">&quot;..&quot;</span></span></span><br><span class="line"><span class="tag">           <span class="attr">review</span>=<span class="string">&quot;https://android-review.googlesource.com/&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">default</span> <span class="attr">revision</span>=<span class="string">&quot;main&quot;</span></span></span><br><span class="line"><span class="tag">           <span class="attr">remote</span>=<span class="string">&quot;aosp&quot;</span></span></span><br><span class="line"><span class="tag">           <span class="attr">sync-j</span>=<span class="string">&quot;4&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">manifest-server</span> <span class="attr">url</span>=<span class="string">&quot;http://android-smartsync.corp.google.com/android.googlesource.com/manifestserver&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">superproject</span> <span class="attr">name</span>=<span class="string">&quot;platform/superproject/main&quot;</span> <span class="attr">remote</span>=<span class="string">&quot;aosp&quot;</span>/&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">contactinfo</span> <span class="attr">bugurl</span>=<span class="string">&quot;go/repo-bug&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/make&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;CleanSpec.mk&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/CleanSpec.mk&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;buildspec.mk.default&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/buildspec.mk.default&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;core&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/core&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;envsetup.sh&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/envsetup.sh&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;target&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/target&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;tools&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/tools&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/bazel&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/bazel&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;bazel.WORKSPACE&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;WORKSPACE&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;bazel.BUILD&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;BUILD&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/bazel_common_rules&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/bazel_common_rules&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/blueprint&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/blueprint&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk,tradefed&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/pesto&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/pesto&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/release&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/release&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk,tradefed&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/soong&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build/soong&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk,tradefed&quot;</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;root.bp&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;Android.bp&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;bootstrap.bash&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;bootstrap.bash&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;art&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/art&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;bionic&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/bionic&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> /&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- ...... --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;external/OpenCL-Headers&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/external/OpenCL-Headers&quot;</span> /&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- ...... --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;trusty/vendor/google/aosp&quot;</span> <span class="attr">name</span>=<span class="string">&quot;trusty/vendor/google/aosp&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;trusty&quot;</span> &gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">copyfile</span> <span class="attr">src</span>=<span class="string">&quot;lk_inc.mk&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;lk_inc.mk&quot;</span> /&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!-- END open-source projects --&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">repo-hooks</span> <span class="attr">in-project</span>=<span class="string">&quot;platform/tools/repohooks&quot;</span> <span class="attr">enabled-list</span>=<span class="string">&quot;pre-upload&quot;</span> /&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!--</span></span><br><span class="line"><span class="comment">    Merge marker to make it easier for git to merge AOSP-only manifest</span></span><br><span class="line"><span class="comment">    changes to a custom manifest.  Please keep this marker here and</span></span><br><span class="line"><span class="comment">    refrain from making changes on or below it.</span></span><br><span class="line"><span class="comment">  --&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>这里面有上百行，这里中间有一大部分省略掉了。接下来我们一部分部分了解。</p>
<ul>
<li>remote标签</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">remote</span>  <span class="attr">name</span>=<span class="string">&quot;aosp&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">fetch</span>=<span class="string">&quot;..&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">review</span>=<span class="string">&quot;https://android-review.googlesource.com/&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>

<p>描述了远程仓库的基本信息。name描述的是一个远程仓库的名称，通常我们看到的命名是origin。fetch用作项目名称的前缘，在构造项目仓库远程地址时使用到。review描述的是用作code review的server地址。</p>
<ul>
<li>default标签</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">default</span> <span class="attr">revision</span>=<span class="string">&quot;main&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">remote</span>=<span class="string">&quot;aosp&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">sync-j</span>=<span class="string">&quot;4&quot;</span> /&gt;</span></span><br></pre></td></tr></table></figure>

<p>default标签的定义的属性，将作为标签的默认属性，在标签中，也可以重写这些属性。属性revision表示当前的版本，也就是我们俗称的分支。属性remote描述的是默认使用的远程仓库名称，即标签中name的属性值。属性sync-j表示在同步远程代码时，并发的任务数量，配置高的机器可以将这个值调大.</p>
<ul>
<li>project标签</li>
</ul>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;build/make&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/build&quot;</span> <span class="attr">groups</span>=<span class="string">&quot;pdk&quot;</span> &gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;CleanSpec.mk&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/CleanSpec.mk&quot;</span> /&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;buildspec.mk.default&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/buildspec.mk.default&quot;</span> /&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;core&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/core&quot;</span> /&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;envsetup.sh&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/envsetup.sh&quot;</span> /&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;target&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/target&quot;</span> /&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">linkfile</span> <span class="attr">src</span>=<span class="string">&quot;tools&quot;</span> <span class="attr">dest</span>=<span class="string">&quot;build/tools&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure>

<p>每一个repo管理的git库，就是对应到一个&lt;project&gt;标签，path描述的是项目相对于远程仓库URL的路径，同时将作为对应的git库在本地代码的路径; name用于定义项目名称，命名方式采用的是整个项目URL的相对地址。 如，AOSP项目的URL为<code>https://android.googlesource.com/</code>，命名为platform&#x2F;build的git库，访问的URL就是<code>https://android.googlesource.com/platform/build</code></p>
<p>如果需要新增或替换一些git库，可以通过修改default.xml来实现，repo会根据配置信息，自动化管理。但直接对default.xml的定制，可能会导致下一次更新项目清单时，与远程default.xml发生冲突。 因此，repo提供了一个种更为灵活的定制方式——<code>local_manifests</code> 。所有的定制是遵循default.xml规范的，文件名可以自定义，如local_manifest.xml, another_local_manifest.xml等， 将定制的XML放在新建的.repo&#x2F;local_manifests子目录即可。repo会遍历.repo&#x2F;local_manifests目录下的所有*.xml文件，最终与default.xml合并成一个总的项目清单文件manifest.xml。</p>
<p>local_manifests的修改示例如下：</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">$ ls .repo/local_manifests</span><br><span class="line">local_manifest.xml</span><br><span class="line">another_local_manifest.xml</span><br><span class="line"></span><br><span class="line">$ cat .repo/local_manifests/local_manifest.xml</span><br><span class="line"><span class="meta">&lt;?xml version=<span class="string">&quot;1.0&quot;</span> encoding=<span class="string">&quot;UTF-8&quot;</span>?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">manifest</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;manifest&quot;</span> <span class="attr">name</span>=<span class="string">&quot;tools/manifest&quot;</span> /&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">project</span> <span class="attr">path</span>=<span class="string">&quot;platform-manifest&quot;</span> <span class="attr">name</span>=<span class="string">&quot;platform/manifest&quot;</span> /&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">manifest</span>&gt;</span></span><br></pre></td></tr></table></figure>

<h2 id="2-repo脚本库-repo-repo"><a href="#2-repo脚本库-repo-repo" class="headerlink" title="2. repo脚本库(.repo&#x2F;repo)"></a><font size=3>2. repo脚本库(.repo&#x2F;repo)</font></h2><p>repo对git命令进行了封装，提供了一套repo的命令集(包括init, sync等)，所有repo管理的自动化实现也都包含在这个git库中。 在第一次初始化的时候，repo会从远程把这个git库下载到本地。</p>
<h2 id="3-仓库目录和工作目录"><a href="#3-仓库目录和工作目录" class="headerlink" title="3. 仓库目录和工作目录"></a><font size=3>3. 仓库目录和工作目录</font></h2><p>仓库目录保存的是历史信息和修改记录，工作目录保存的是当前版本的信息。一般来说，一个项目的Git仓库目录（默认为.git目录）是位于工作目录下面的，但是Git支持将一个项目的Git仓库目录和工作目录分开来存放。 对于repo管理而言，既有分开存放，也有位于工作目录存放的:</p>
<ul>
<li>manifests： 仓库目录有两份拷贝，一份位于工作目录(.repo&#x2F;manifests)的.git目录下，另一份独立存放于.repo&#x2F;manifests.git</li>
<li>repo：仓库目录位于工作目录(.repo&#x2F;repo)的.git目录下</li>
<li>project：所有被管理git库的仓库目录都是分开存放的，位于.repo&#x2F;projects目录下。同时，也会保留工作目录的.git，但里面所有的文件都是到.repo的链接。这样，即做到了分开存放，也兼容了在工作目录下的所有git命令。</li>
</ul>
<p><strong>既然.repo目录下保存了项目的所有信息，所有要拷贝一个项目时，只是需要拷贝这个目录就可以了。repo支持从本地已有的.repo中恢复原有的项目。</strong></p>
<blockquote>
<p>参考资料：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_42107504/article/details/140709590">repo 工具安装和使用教程（windows+gitee)_repo安装-CSDN博客</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/weixin_64124795/article/details/131599506">Repo工作原理及常用命令总结——2023.07-CSDN博客</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jb51.net/article/131935.htm">Android源代码仓库及其管理工具Repo分析详解_Android_脚本之家 (jb51.net)</a></p>
<p><a target="_blank" rel="noopener" href="https://www.jb51.net/article/232003.htm">Repo工作原理和使用介绍_Android_脚本之家 (jb51.net)</a></p>
</blockquote>

    </div>

    
    
    

    <footer class="post-footer">




    <div>
        
            <div style="text-align:center;color: #ccc;font-size:14px;">
            ----------本文结束
            <i class="fas fa-fan fa-spin" style="color: #FF1493; font-size: 1rem"></i>
            感谢您的阅读----------
            </div>
        
    </div>





  
  <div class="my_post_copyright"> 
    <p><span>文章标题:</span><a href="/post/d94f8cde.html">LV03-repo-02-repo基本原理</a></p>
    <p><span>文章作者:</span><a href="/" title="欢迎访问 《苏木》 的学习笔记">苏木</a></p>
    <p><span>发布时间:</span>2024年10月26日 - 22:40</p>
    <p><span>最后更新:</span>2025年06月14日 - 00:25</p>
    <p><span>原始链接:</span><a href="/post/d94f8cde.html" title="LV03-repo-02-repo基本原理">https://sumumm.github.io/post/d94f8cde.html</a></p>
    <p><span>许可协议:</span><i class="fab fa-creative-commons"></i> <a rel="license" href= "https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">署名-非商业性使用-禁止演绎 4.0 国际</a> 转载请保留原文链接及作者。</p>  
  </div>
  


          <div class="post-tags">
              <a href="/tags/Git/" rel="tag"><i class="fa fa-tag"></i> Git</a>
          </div>

        

          <div class="post-nav">
            <div class="post-nav-item">
                <a href="/post/58627ebd.html" rel="prev" title="LV03-repo-03-从0搭建一个repo管理的项目">
                  <i class="fa fa-angle-left"></i> LV03-repo-03-从0搭建一个repo管理的项目
                </a>
            </div>
            <div class="post-nav-item">
                <a href="/post/1eba3c88.html" rel="next" title="LV03-repo-01-repo简介与安装">
                  LV03-repo-01-repo简介与安装 <i class="fa fa-angle-right"></i>
                </a>
            </div>
          </div>
    </footer>
  </article>
</div>






</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">

  <div class="copyright">
    &copy; 2017 – 
    <span itemprop="copyrightYear">2025</span>
    <span class="with-love">
      <i class="fa fa-heart"></i>
    </span>
    <span class="author" itemprop="copyrightHolder">苏木</span>
  </div>
<div class="wordcount">
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-chart-line"></i>
    </span>
      <span>站点总字数：</span>
    <span title="站点总字数">3.7m</span>
  </span>
  <span class="post-meta-item">
    <span class="post-meta-item-icon">
      <i class="fa fa-coffee"></i>
    </span>
      <span>站点阅读时长 &asymp;</span>
    <span title="站点阅读时长">225:26</span>
  </span>
</div>




    <span id="sitetime"></span>
    <script defer language=javascript>
        function siteTime()
        {
            window.setTimeout("siteTime()", 1000);
            var seconds = 1000;
            var minutes = seconds * 60;
            var hours = minutes * 60;
            var days = hours * 24;
            var years = days * 365;
            var today = new Date();
            var todayYear = today.getFullYear();
            var todayMonth = today.getMonth()+1;
            var todayDate = today.getDate();
            var todayHour = today.getHours();
            var todayMinute = today.getMinutes();
            var todaySecond = today.getSeconds();
            /*==================================================
            Date.UTC() -- 返回date对象距世界标准时间(UTC)1970年1月1日午夜之间的毫秒数(时间戳)
            year        - 作为date对象的年份，为4位年份值
            month       - 0-11之间的整数，做为date对象的月份
            day         - 1-31之间的整数，做为date对象的天数
            hours       - 0(午夜24点)-23之间的整数，做为date对象的小时数
            minutes     - 0-59之间的整数，做为date对象的分钟数
            seconds     - 0-59之间的整数，做为date对象的秒数
            microseconds - 0-999之间的整数，做为date对象的毫秒数
            ==================================================*/
            var t1 = Date.UTC(2017, 
                              5, 
                              19, 
                              0, 
                              0, 
                              0); //北京时间
            var t2 = Date.UTC(todayYear,todayMonth,todayDate,todayHour,todayMinute,todaySecond);
            var diff = t2-t1;
            var diffYears = Math.floor(diff/years);
            var diffDays = Math.floor((diff/days)-diffYears*365);
            var diffHours = Math.floor((diff-(diffYears*365+diffDays)*days)/hours);
            var diffMinutes = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours)/minutes);
            var diffSeconds = Math.floor((diff-(diffYears*365+diffDays)*days-diffHours*hours-diffMinutes*minutes)/seconds);
            document.getElementById("sitetime").innerHTML="已在这里 "+diffYears+" 年 "+diffDays+" 天 "+diffHours+" 小时 "+diffMinutes+" 分钟 "+diffSeconds+" 秒";
        }
        siteTime();
    </script>



    </div>
  </footer>

  
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>
  <div class="reading-progress-bar"></div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/next-theme-pjax/0.6.0/pjax.min.js" integrity="sha256-vxLn1tSKWD4dqbMRyv940UYw4sXgMtYcK6reefzZrao=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/fancyapps-ui/5.0.28/fancybox/fancybox.umd.js" integrity="sha256-ytMJGN3toR+a84u7g7NuHm91VIR06Q41kMWDr2pq7Zo=" crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lozad.js/1.16.0/lozad.min.js" integrity="sha256-mOFREFhqmHeQbXpK2lp4nA3qooVgACfh88fpJftLBbc=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script><script src="/js/pjax.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>




  <script src="/js/third-party/fancybox.js"></script>

  <script src="/js/third-party/pace.js"></script>


  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>


 
        <div id="click-show-text"
            data-mobile = false
            data-text = 富强,民主,文明,和谐,自由,平等,公正,法制,爱国,敬业,诚信,友善
            data-fontsize = 15px
            data-random= false>
        </div>
       

      
        <script async src=https://cdn.jsdelivr.net/npm/hexo-next-mouse-effect@latest/click/showText.js></script>
      

      
    




    <script async src="/js/fancybox_param.js"></script>





<!-- APlayer本体 -->



</body>
</html>
